From cef22ae84cc531c61e635bf8d69b62bdc1eb3c4f Mon Sep 17 00:00:00 2001 From: Gator96100 Date: Sun, 22 Nov 2020 22:46:58 +0100 Subject: [PATCH 01/52] Modified the tasks and launch configuration for ProxSpace --- .vscode/launch.json | 31 +++++++++++++++-- .vscode/tasks.json | 84 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 112 insertions(+), 3 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index d3d86ee80..f6a030c7c 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -18,13 +18,21 @@ "text": "-enable-pretty-printing", "ignoreFailures": true } - ] + ], + "windows": { + "processId": "${input:ProcessIDWindows}", + "miDebuggerPath": "${workspaceFolder}/../../msys2/mingw64/bin/gdb.exe", + "externalConsole": true,//for ProxSpace externalConsole=true is required, because the internal cosole stops updating after a while + "environment": [{ + "name": "PATH","value": "${workspaceFolder}/../../msys2/mingw64/bin;${workspaceFolder}/../../msys2/usr/local/bin;${workspaceFolder}/../../msys2/usr/bin;${workspaceFolder}/../../msys2/bin" + }] + } },{ "name": "(gdb) Build & Launch", "type": "cppdbg", "request": "launch", "program": "${cwd}/client/proxmark3", - "args": ["/dev/ttyACM0"], + "args": ["/dev/ttyACM0"], "stopAtEntry": false, "cwd": "${workspaceFolder}", "environment": [], @@ -38,7 +46,15 @@ } ], "preLaunchTask": "client: Debug: clean & make", - "miDebuggerPath": "/usr/bin/gdb" + "miDebuggerPath": "/usr/bin/gdb", + "windows": { + "args": ["COM5"], + "miDebuggerPath": "${workspaceFolder}/../../msys2/mingw64/bin/gdb.exe", + "externalConsole": true,//for ProxSpace externalConsole=true is required, because the internal cosole stops updating after a while + "environment": [{ + "name": "PATH","value": "${workspaceFolder}/../../msys2/mingw64/bin;${workspaceFolder}/../../msys2/usr/local/bin;${workspaceFolder}/../../msys2/usr/bin;${workspaceFolder}/../../msys2/bin" + }] + } } ], "inputs": [ @@ -50,6 +66,15 @@ "args": { "command": "pgrep -n proxmark3", } + + },{ + "id": "ProcessIDWindows", + "type": "command", + "command": "shellCommand.execute", + "args": { + "command": "${workspaceFolder}/../../runme64.bat -c \"cat /proc/$(pgrep -n proxmark3)/winpid\"", + } + } ] } \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 155aa80cd..7354a8a2d 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -7,6 +7,18 @@ "label": "all: Make & run", "type": "shell", "command": "make -j && ./pm3", + "windows": { + "options": { + "cwd": "${workspaceFolder}/../..", + "shell": { + "executable": "${workspaceFolder}/../../runme64.bat", + "args": [ + "-c \"cd ${workspaceFolderBasename} &&" + ], + + } + } + }, "problemMatcher": [ "$gcc" ], @@ -19,6 +31,18 @@ "label": "choose: Make", "type": "shell", "command": "make ${input:componentType} -j", + "windows": { + "options": { + "cwd": "${workspaceFolder}/../..", + "shell": { + "executable": "${workspaceFolder}/../../runme64.bat", + "args": [ + "-c \"cd ${workspaceFolderBasename} &&" + ], + + } + } + }, "problemMatcher": [ "$gcc" ], @@ -28,6 +52,18 @@ "label": "client: Debug: make", "type": "shell", "command": "make client -j DEBUG=1", + "windows": { + "options": { + "cwd": "${workspaceFolder}/../..", + "shell": { + "executable": "${workspaceFolder}/../../runme64.bat", + "args": [ + "-c \"cd ${workspaceFolderBasename} &&" + ], + + } + } + }, "problemMatcher": [ "$gcc" ], @@ -37,6 +73,18 @@ "label": "client: Debug: clean & make", "type": "shell", "command": "make client/clean && make client -j DEBUG=1", + "windows": { + "options": { + "cwd": "${workspaceFolder}/../..", + "shell": { + "executable": "${workspaceFolder}/../../runme64.bat", + "args": [ + "-c \"cd ${workspaceFolderBasename} &&" + ], + + } + } + }, "problemMatcher": [ "$gcc" ], @@ -46,18 +94,54 @@ "label": "fullimage: Make & Flash", "type": "shell", "command": "make fullimage && ./pm3-flash-fullimage", + "windows": { + "options": { + "cwd": "${workspaceFolder}/../..", + "shell": { + "executable": "${workspaceFolder}/../../runme64.bat", + "args": [ + "-c \"cd ${workspaceFolderBasename} &&" + ], + + } + } + }, "problemMatcher": [] }, { "label": "BOOTROM: Make & Flash", "type": "shell", "command": "make bootrom && ./pm3-flash-bootrom", + "windows": { + "options": { + "cwd": "${workspaceFolder}/../..", + "shell": { + "executable": "${workspaceFolder}/../../runme64.bat", + "args": [ + "-c \"cd ${workspaceFolderBasename} &&" + ], + + } + } + }, "problemMatcher": [] }, { "label": "Run client", "type": "shell", "command": "./pm3", + "windows": { + "options": { + "cwd": "${workspaceFolder}/../..", + "shell": { + "executable": "${workspaceFolder}/../../runme64.bat", + "args": [ + "-c \"cd ${workspaceFolderBasename} &&" + ], + + } + } + }, "problemMatcher": [] } ], From a11e2ed4a6d73dc025ce17fd47b13b6777eb4c35 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Sun, 22 Nov 2020 23:42:00 +0100 Subject: [PATCH 02/52] hf mfdes info - cmk textual ( #1062 ) --- client/src/cmdhfmf.c | 2 +- client/src/cmdhfmfdes.c | 206 +++++++++++++++++++++++----------------- 2 files changed, 118 insertions(+), 90 deletions(-) diff --git a/client/src/cmdhfmf.c b/client/src/cmdhfmf.c index 82fc8a1c4..481a5ce14 100644 --- a/client/src/cmdhfmf.c +++ b/client/src/cmdhfmf.c @@ -5444,7 +5444,7 @@ static command_t CommandTable[] = { {"esave", CmdHF14AMfESave, IfPm3Iso14443a, "Save to file emul dump"}, {"eset", CmdHF14AMfESet, IfPm3Iso14443a, "Set simulator memory block"}, {"eview", CmdHF14AMfEView, IfPm3Iso14443a, "View emul memory"}, - {"-----------", CmdHelp, IfPm3Iso14443a, "----------------------- " _CYAN_("magic") " -----------------------"}, + {"-----------", CmdHelp, IfPm3Iso14443a, "----------------------- " _CYAN_("magic gen1") " -----------------------"}, {"cgetblk", CmdHF14AMfCGetBlk, IfPm3Iso14443a, "Read block"}, {"cgetsc", CmdHF14AMfCGetSc, IfPm3Iso14443a, "Read sector"}, {"cload", CmdHF14AMfCLoad, IfPm3Iso14443a, "Load dump"}, diff --git a/client/src/cmdhfmfdes.c b/client/src/cmdhfmfdes.c index 4c4f713f7..a69aa0d27 100644 --- a/client/src/cmdhfmfdes.c +++ b/client/src/cmdhfmfdes.c @@ -395,9 +395,9 @@ static char *getCardSizeStr(uint8_t fsize) { // is LSB set? if (fsize & 1) - snprintf(retStr, sizeof(buf), "0x%02X (" _GREEN_("%d - %d bytes") ")", fsize, usize, lsize); + snprintf(retStr, sizeof(buf), "0x%02X ( " _GREEN_("%d - %d bytes") " )", fsize, usize, lsize); else - snprintf(retStr, sizeof(buf), "0x%02X (" _GREEN_("%d bytes") ")", fsize, lsize); + snprintf(retStr, sizeof(buf), "0x%02X ( " _GREEN_("%d bytes") " )", fsize, lsize); return buf; } @@ -407,14 +407,14 @@ static char *getProtocolStr(uint8_t id, bool hw) { char *retStr = buf; if (id == 0x04) { - snprintf(retStr, sizeof(buf), "0x%02X (" _YELLOW_("ISO 14443-3 MIFARE, 14443-4") ")", id); + snprintf(retStr, sizeof(buf), "0x%02X ( " _YELLOW_("ISO 14443-3 MIFARE, 14443-4") " )", id); } else if (id == 0x05) { if (hw) - snprintf(retStr, sizeof(buf), "0x%02X (" _YELLOW_("ISO 14443-2, 14443-3") ")", id); + snprintf(retStr, sizeof(buf), "0x%02X ( " _YELLOW_("ISO 14443-2, 14443-3") " )", id); else - snprintf(retStr, sizeof(buf), "0x%02X (" _YELLOW_("ISO 14443-3, 14443-4") ")", id); + snprintf(retStr, sizeof(buf), "0x%02X ( " _YELLOW_("ISO 14443-3, 14443-4") " )", id); } else { - snprintf(retStr, sizeof(buf), "0x%02X (" _YELLOW_("Unknown") ")", id); + snprintf(retStr, sizeof(buf), "0x%02X ( " _YELLOW_("Unknown") " )", id); } return buf; } @@ -425,19 +425,21 @@ static char *getVersionStr(uint8_t major, uint8_t minor) { char *retStr = buf; if (major == 0x00) - snprintf(retStr, sizeof(buf), "%x.%x (" _GREEN_("DESFire MF3ICD40") ")", major, minor); + snprintf(retStr, sizeof(buf), "%x.%x ( " _GREEN_("DESFire MF3ICD40") " )", major, minor); else if (major == 0x01 && minor == 0x00) - snprintf(retStr, sizeof(buf), "%x.%x (" _GREEN_("DESFire EV1") ")", major, minor); + snprintf(retStr, sizeof(buf), "%x.%x ( " _GREEN_("DESFire EV1") " )", major, minor); else if (major == 0x12 && minor == 0x00) - snprintf(retStr, sizeof(buf), "%x.%x (" _GREEN_("DESFire EV2") ")", major, minor); + snprintf(retStr, sizeof(buf), "%x.%x ( " _GREEN_("DESFire EV2") " )", major, minor); + else if (major == 0x42 && minor == 0x00) + snprintf(retStr, sizeof(buf), "%x.%x ( " _GREEN_("DESFire EV2") " )", major, minor); else if (major == 0x33 && minor == 0x00) - snprintf(retStr, sizeof(buf), "%x.%x (" _GREEN_("DESFire EV3") ")", major, minor); + snprintf(retStr, sizeof(buf), "%x.%x ( " _GREEN_("DESFire EV3") " )", major, minor); else if (major == 0x30 && minor == 0x00) - snprintf(retStr, sizeof(buf), "%x.%x (" _GREEN_("DESFire Light") ")", major, minor); + snprintf(retStr, sizeof(buf), "%x.%x ( " _GREEN_("DESFire Light") " )", major, minor); else if (major == 0x10 && minor == 0x00) - snprintf(retStr, sizeof(buf), "%x.%x (" _GREEN_("NTAG413DNA") ")", major, minor); + snprintf(retStr, sizeof(buf), "%x.%x ( " _GREEN_("NTAG413DNA") " )", major, minor); else - snprintf(retStr, sizeof(buf), "%x.%x (" _YELLOW_("Unknown") ")", major, minor); + snprintf(retStr, sizeof(buf), "%x.%x ( " _YELLOW_("Unknown") " )", major, minor); return buf; //04 01 01 01 00 1A 05 @@ -1381,15 +1383,43 @@ static int handler_desfire_signature(uint8_t *signature, size_t *signature_len) return res; } -// --- KEY SETTING -static int desfire_print_keysetting(uint8_t key_settings, uint8_t num_keys, int algo) { +// --- KEY VERSION +static int desfire_print_keyversion(uint8_t key_idx, uint8_t key_version) { + PrintAndLogEx(SUCCESS, " Key [%u] Version : %d (0x%02x)", key_idx, key_version, key_version); + return PM3_SUCCESS; +} + +static int handler_desfire_keyversion(uint8_t curr_key, uint8_t *num_versions) { + if (num_versions == NULL) { + PrintAndLogEx(DEBUG, "NUM_VERSIONS=NULL"); + return PM3_EINVARG; + } + sAPDU apdu = {0x90, MFDES_GET_KEY_VERSION, 0x00, 0x00, 0x01, &curr_key}; //0x64 + uint32_t recv_len = 0; + uint16_t sw = 0; + int res = send_desfire_cmd(&apdu, false, num_versions, &recv_len, &sw, 0, true); + + if (res != PM3_SUCCESS) + return res; + + if (sw != status(MFDES_S_OPERATION_OK)) + return PM3_ESOFT; + + return res; +} + +// --- KEY SETTING Application Master Key +static int desfire_print_amk_keysetting(uint8_t key_settings, uint8_t num_keys, int algo) { PrintAndLogEx(SUCCESS, " AID Key settings : 0x%02x", key_settings); // 2 MSB denotes const char *str = " Max key number and type : %d, " _YELLOW_("%s"); - if (algo == MFDES_ALGO_DES) PrintAndLogEx(SUCCESS, str, num_keys & 0x3F, "(3)DES"); - else if (algo == MFDES_ALGO_AES) PrintAndLogEx(SUCCESS, str, num_keys & 0x3F, "AES"); - else if (algo == MFDES_ALGO_3K3DES) PrintAndLogEx(SUCCESS, str, num_keys & 0x3F, "3K3DES"); + if (algo == MFDES_ALGO_DES) + PrintAndLogEx(SUCCESS, str, num_keys & 0x3F, "(3)DES"); + else if (algo == MFDES_ALGO_AES) + PrintAndLogEx(SUCCESS, str, num_keys & 0x3F, "AES"); + else if (algo == MFDES_ALGO_3K3DES) + PrintAndLogEx(SUCCESS, str, num_keys & 0x3F, "3K3DES"); //PrintAndLogEx(SUCCESS, " Max number of keys in AID : %d", num_keys & 0x3F); PrintAndLogEx(INFO, "-------------------------------------------------------------"); @@ -1408,14 +1438,69 @@ static int desfire_print_keysetting(uint8_t key_settings, uint8_t num_keys, int PrintAndLogEx(SUCCESS, " -- All keys (except AMK,see Bit0) within this application are frozen"); break; default: - PrintAndLogEx(SUCCESS, " -- Authentication with the specified key is necessary to change any key.\nA change key and a PICC master key (CMK) can only be changed after authentication with the master key.\nFor keys other then the master or change key, an authentication with the same key is needed."); + PrintAndLogEx(SUCCESS, + " -- Authentication with the specified key is necessary to change any key.\n" + "A change key and a PICC master key (CMK) can only be changed after authentication with the master key.\n" + "For keys other then the master or change key, an authentication with the same key is needed." + ); break; } - PrintAndLogEx(SUCCESS, " [0x08] Configuration changeable : %s", (key_settings & (1 << 3)) ? _GREEN_("YES") : "NO"); - PrintAndLogEx(SUCCESS, " [0x04] AMK required for create/delete : %s", (key_settings & (1 << 2)) ? "NO" : "YES"); - PrintAndLogEx(SUCCESS, " [0x02] Directory list access with AMK : %s", (key_settings & (1 << 1)) ? "NO" : "YES"); - PrintAndLogEx(SUCCESS, " [0x01] AMK is changeable : %s", (key_settings & (1 << 0)) ? _GREEN_("YES") : "NO"); + PrintAndLogEx(SUCCESS, " [1000] AMK Configuration changeable : %s", (key_settings & (1 << 3)) ? _GREEN_("YES") : "NO (frozen)"); + PrintAndLogEx(SUCCESS, " [0100] AMK required for create/delete : %s", (key_settings & (1 << 2)) ? "NO" : "YES"); + PrintAndLogEx(SUCCESS, " [0010] Directory list access with AMK : %s", (key_settings & (1 << 1)) ? "NO" : "YES"); + PrintAndLogEx(SUCCESS, " [0001] AMK is changeable : %s", (key_settings & (1 << 0)) ? _GREEN_("YES") : "NO (frozen)"); + return PM3_SUCCESS; +} + +// --- KEY SETTING PICC Master Key (CMK) +static int desfire_print_piccmk_keysetting(uint8_t key_settings, uint8_t num_keys, int algo) { + //PrintAndLogEx(INFO, "--- " _CYAN_("PICC Master Key (CMK) settings")); + // number of Master keys (0x01) + PrintAndLogEx(SUCCESS, " Number of Masterkeys : " _YELLOW_("%u"), (num_keys & 0x3F)); + const char *str = " Operation of PICC master key : " _YELLOW_("%s"); + + if (algo == MFDES_ALGO_DES) + PrintAndLogEx(SUCCESS, str, "(3)DES"); + else if (algo == MFDES_ALGO_AES) + PrintAndLogEx(SUCCESS, str, "AES"); + else if (algo == MFDES_ALGO_3K3DES) + PrintAndLogEx(SUCCESS, str, "3K3DES"); + + uint8_t cmk_num_versions = 0; + if (handler_desfire_keyversion(0, &cmk_num_versions) == PM3_SUCCESS) { + PrintAndLogEx(SUCCESS, " PICC Master key Version : " _YELLOW_("%d (0x%02x)"), cmk_num_versions, cmk_num_versions); + } + + PrintAndLogEx(INFO, " ----------------------------------------------------------"); + + // Authentication tests + int res = test_desfire_authenticate(); + if (res == PM3_SUCCESS) + PrintAndLogEx(SUCCESS, " [0x0A] Authenticate : %s", (res == PM3_SUCCESS) ? _YELLOW_("YES") : "NO"); + + res = test_desfire_authenticate_iso(); + if (res == PM3_SUCCESS) + PrintAndLogEx(SUCCESS, " [0x1A] Authenticate ISO : %s", (res == PM3_SUCCESS) ? _YELLOW_("YES") : "NO"); + + res = test_desfire_authenticate_aes(); + if (res == PM3_SUCCESS) + PrintAndLogEx(SUCCESS, " [0xAA] Authenticate AES : %s", (res == PM3_SUCCESS) ? _YELLOW_("YES") : "NO"); + + PrintAndLogEx(INFO, "-------------------------------------------------------------"); + PrintAndLogEx(INFO, " Key setting: 0x%02X [%c%c%c%c]", + key_settings, + (key_settings & (1 << 3)) ? '1' : '0', + (key_settings & (1 << 2)) ? '1' : '0', + (key_settings & (1 << 1)) ? '1' : '0', + (key_settings & (1 << 0)) ? '1' : '0' + ); + + PrintAndLogEx(SUCCESS, " [1...] CMK Configuration changeable : %s", (key_settings & (1 << 3)) ? _GREEN_("YES") : "NO (frozen)"); + PrintAndLogEx(SUCCESS, " [.1..] CMK required for create/delete : %s", (key_settings & (1 << 2)) ? _GREEN_("NO") : "YES"); + PrintAndLogEx(SUCCESS, " [..1.] Directory list access with CMK : %s", (key_settings & (1 << 1)) ? _GREEN_("NO") : "YES"); + PrintAndLogEx(SUCCESS, " [...1] CMK is changeable : %s", (key_settings & (1 << 0)) ? _GREEN_("YES") : "NO (frozen)"); + return PM3_SUCCESS; } @@ -1445,31 +1530,6 @@ static int handler_desfire_getkeysettings(uint8_t *key_settings, uint8_t *num_ke return res; } -// --- KEY VERSION -static int desfire_print_keyversion(uint8_t key_idx, uint8_t key_version) { - PrintAndLogEx(SUCCESS, " Key [%u] Version : %d (0x%02x)", key_idx, key_version, key_version); - return PM3_SUCCESS; -} - -static int handler_desfire_keyversion(uint8_t curr_key, uint8_t *num_versions) { - if (num_versions == NULL) { - PrintAndLogEx(DEBUG, "NUM_VERSIONS=NULL"); - return PM3_EINVARG; - } - sAPDU apdu = {0x90, MFDES_GET_KEY_VERSION, 0x00, 0x00, 0x01, &curr_key}; //0x64 - uint32_t recv_len = 0; - uint16_t sw = 0; - int res = send_desfire_cmd(&apdu, false, num_versions, &recv_len, &sw, 0, true); - - if (res != PM3_SUCCESS) - return res; - - if (sw != status(MFDES_S_OPERATION_OK)) - return PM3_ESOFT; - - return res; -} - static int handler_desfire_getuid(uint8_t *uid) { if (uid == NULL) { PrintAndLogEx(DEBUG, "UID=NULL"); @@ -2006,57 +2066,24 @@ static int handler_desfire_create_backup_file(mfdes_file_t *file) { static int getKeySettings(uint8_t *aid) { if (aid == NULL) return PM3_EINVARG; + uint8_t num_keys = 0; + uint8_t key_setting = 0; int res = 0; if (memcmp(aid, "\x00\x00\x00", 3) == 0) { - + // CARD MASTER KEY //PrintAndLogEx(INFO, "--- " _CYAN_("CMK - PICC, Card Master Key settings")); // KEY Settings - AMK - uint8_t num_keys = 0; - uint8_t key_setting = 0; mifare_des_authalgo_t algo = MFDES_ALGO_DES; res = key_setting_to_algo(aid, &key_setting, &algo, &num_keys); if (res == PM3_SUCCESS) { - // number of Master keys (0x01) - PrintAndLogEx(SUCCESS, " Number of Masterkeys : " _YELLOW_("%u"), (num_keys & 0x3F)); - - PrintAndLogEx(SUCCESS, " [0x08] Configuration changeable : %s", (key_setting & (1 << 3)) ? _GREEN_("YES") : "NO"); - PrintAndLogEx(SUCCESS, " [0x04] CMK required for create/delete : %s", (key_setting & (1 << 2)) ? _GREEN_("YES") : "NO"); - PrintAndLogEx(SUCCESS, " [0x02] Directory list access with CMK : %s", (key_setting & (1 << 1)) ? _GREEN_("YES") : "NO"); - PrintAndLogEx(SUCCESS, " [0x01] CMK is changeable : %s", (key_setting & (1 << 0)) ? _GREEN_("YES") : "NO"); + desfire_print_piccmk_keysetting(key_setting, num_keys, algo); } else { - PrintAndLogEx(WARNING, _RED_(" Can't read Application Master key settings")); + PrintAndLogEx(WARNING, _RED_(" Can't read PICC Master key settings")); } - const char *str = " Operation of PICC master key : " _YELLOW_("%s"); - - if (algo == MFDES_ALGO_DES) PrintAndLogEx(SUCCESS, str, "(3)DES"); - else if (algo == MFDES_ALGO_AES) PrintAndLogEx(SUCCESS, str, "AES"); - else if (algo == MFDES_ALGO_3K3DES) PrintAndLogEx(SUCCESS, str, "3K3DES"); - - uint8_t cmk_num_versions = 0; - if (handler_desfire_keyversion(0, &cmk_num_versions) == PM3_SUCCESS) { - PrintAndLogEx(SUCCESS, " PICC Master key Version : " _YELLOW_("%d (0x%02x)"), cmk_num_versions, cmk_num_versions); - PrintAndLogEx(INFO, " ----------------------------------------------------------"); - } - - // Authentication tests - res = test_desfire_authenticate(); - if (res == PM3_ETIMEOUT) return res; - PrintAndLogEx(SUCCESS, " [0x0A] Authenticate : %s", (res == PM3_SUCCESS) ? _YELLOW_("YES") : "NO"); - - res = test_desfire_authenticate_iso(); - if (res == PM3_ETIMEOUT) return res; - PrintAndLogEx(SUCCESS, " [0x1A] Authenticate ISO : %s", (res == PM3_SUCCESS) ? _YELLOW_("YES") : "NO"); - - res = test_desfire_authenticate_aes(); - if (res == PM3_ETIMEOUT) return res; - PrintAndLogEx(SUCCESS, " [0xAA] Authenticate AES : %s", (res == PM3_SUCCESS) ? _YELLOW_("YES") : "NO"); - - PrintAndLogEx(INFO, "-------------------------------------------------------------"); - } else { // AID - APPLICATION MASTER KEYS @@ -2065,12 +2092,10 @@ static int getKeySettings(uint8_t *aid) { if (res != PM3_SUCCESS) return res; // KEY Settings - AMK - uint8_t num_keys = 0; - uint8_t key_setting = 0; mifare_des_authalgo_t algo = MFDES_ALGO_DES; res = key_setting_to_algo(aid, &key_setting, &algo, &num_keys); if (res == PM3_SUCCESS) { - desfire_print_keysetting(key_setting, num_keys, algo); + desfire_print_amk_keysetting(key_setting, num_keys, algo); } else { PrintAndLogEx(WARNING, _RED_(" Can't read Application Master key settings")); } @@ -3436,6 +3461,8 @@ static int CmdHF14ADesInfo(const char *Cmd) { PrintAndLogEx(SUCCESS, " Production date: week " _GREEN_("%02x") " / " _GREEN_("20%02x"), info.details[12], info.details[13]); PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "--- " _CYAN_("Hardware Information")); + PrintAndLogEx(INFO, " raw: %s", sprint_hex_inrow(info.versionHW, sizeof(info.versionHW))); + PrintAndLogEx(INFO, " Vendor Id: " _YELLOW_("%s"), getTagInfo(info.versionHW[0])); PrintAndLogEx(INFO, " Type: " _YELLOW_("0x%02X"), info.versionHW[1]); PrintAndLogEx(INFO, " Subtype: " _YELLOW_("0x%02X"), info.versionHW[2]); @@ -3444,6 +3471,7 @@ static int CmdHF14ADesInfo(const char *Cmd) { PrintAndLogEx(INFO, " Protocol: %s", getProtocolStr(info.versionHW[6], true)); PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "--- " _CYAN_("Software Information")); + PrintAndLogEx(INFO, " raw: %s", sprint_hex_inrow(info.versionSW, sizeof(info.versionSW))); PrintAndLogEx(INFO, " Vendor Id: " _YELLOW_("%s"), getTagInfo(info.versionSW[0])); PrintAndLogEx(INFO, " Type: " _YELLOW_("0x%02X"), info.versionSW[1]); PrintAndLogEx(INFO, " Subtype: " _YELLOW_("0x%02X"), info.versionSW[2]); From 6c1bdd34570abb8e993a57a0be0a368d7e813d5b Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Mon, 23 Nov 2020 00:43:11 +0100 Subject: [PATCH 03/52] update ps version --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9717c30c7..e61260b54 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,7 @@ On the software side: quite a lot, see the [Changelog file](CHANGELOG.md). This repo compiles nicely on - Proxspace v3.x - - [latest release v3.7](https://github.com/Gator96100/ProxSpace/releases) + - [latest release v3.7.1](https://github.com/Gator96100/ProxSpace/releases) - Windows/mingw environment with Qt5.6.1 & GCC 4.9 - Ubuntu 16.04 -> 20.04 - ParrotOS, Gentoo, Pentoo, Kali, Nethunter, Archlinux, Fedora, Debian From c80c7646da3bff1c064b71909537652718affd61 Mon Sep 17 00:00:00 2001 From: tcprst Date: Sun, 22 Nov 2020 19:33:11 -0500 Subject: [PATCH 04/52] iclass esave, eview - now use cliparser --- client/src/cmdhficlass.c | 149 ++++++++++++++------------------------- 1 file changed, 51 insertions(+), 98 deletions(-) diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index eaf5b5406..c72db32b4 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -71,35 +71,6 @@ static int usage_hf_iclass_sim(void) { PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; } - -static int usage_hf_iclass_esave(void) { - PrintAndLogEx(NORMAL, "Save emulator memory to file."); - PrintAndLogEx(NORMAL, "if not filename is supplied, CSN will be used."); - PrintAndLogEx(NORMAL, "Number of bytes to download defaults to 256. Other value is 2048\n"); - PrintAndLogEx(NORMAL, "Usage: hf iclass esave [h] [f ] [s ]\n"); - PrintAndLogEx(NORMAL, "Options"); - PrintAndLogEx(NORMAL, " h : Show this help"); - PrintAndLogEx(NORMAL, " f : filename of dump"); - PrintAndLogEx(NORMAL, " s : (256|2048) number of bytes to save (default 256)"); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_("\thf iclass esave")); - PrintAndLogEx(NORMAL, _YELLOW_("\thf iclass esave f hf-iclass-dump.bin")); - PrintAndLogEx(NORMAL, _YELLOW_("\thf iclass esave s 2048 f hf-iclass-dump.bin")); - PrintAndLogEx(NORMAL, ""); - return PM3_SUCCESS; -} -static int usage_hf_iclass_eview(void) { - PrintAndLogEx(NORMAL, "It displays emulator memory"); - PrintAndLogEx(NORMAL, "Number of bytes to download defaults to 256. Other value is 2048\n"); - PrintAndLogEx(NORMAL, " Usage: hf iclass eview [s ] "); - PrintAndLogEx(NORMAL, " s : (256|2048) number of bytes to save (default 256)"); - PrintAndLogEx(NORMAL, " v : verbose output"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf iclass eview")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf iclass eview s 2048 v")); - return PM3_SUCCESS; -} static int usage_hf_iclass_decrypt(void) { PrintAndLogEx(NORMAL, "3DES decrypt data\n"); PrintAndLogEx(NORMAL, "This is naive implementation, it tries to decrypt every block after block 6."); @@ -779,7 +750,7 @@ static int CmdHFiClassSim(const char *Cmd) { SendCommandMIX(CMD_HF_ICLASS_SIMULATE, sim_type, numberOfCSNs, 1, CSN, 8); if (sim_type == ICLASS_SIM_MODE_FULL) - PrintAndLogEx(HINT, "Try `" _YELLOW_("hf iclass esave h") "` to save the emulator memory to file"); + PrintAndLogEx(HINT, "Try `" _YELLOW_("hf iclass esave -h") "` to save the emulator memory to file"); break; } } @@ -961,47 +932,36 @@ static int CmdHFiClassELoad(const char *Cmd) { PrintAndLogEx(SUCCESS, "sent %d bytes of data to device emulator memory", bytes_sent); return PM3_SUCCESS; } - static int CmdHFiClassESave(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf iclass esave", + "Save emulator memory to file.\n" + "if filename is not supplied, CSN will be used.", + "hf iclass esave\n" + "hf iclass esave -f hf-iclass-dump\n" + "hf iclass esave -s 2048 -f hf-iclass-dump"); + void *argtable[] = { + arg_param_begin, + arg_str0("f", "file", "", "filename of dumpfile"), + arg_int0("s", "size", "<256|2048>", "number of bytes to save (default 256)"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + int fnlen = 0; char filename[FILE_PATH_SIZE] = {0}; + CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); char *fnameptr = filename; - int len = 0; - uint16_t bytes = 256; - bool errors = false; - uint8_t cmdp = 0; - while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { - switch (tolower(param_getchar(Cmd, cmdp))) { - case 'h': - return usage_hf_iclass_esave(); - case 'f': - len = param_getstr(Cmd, cmdp + 1, filename, FILE_PATH_SIZE); - if (len >= FILE_PATH_SIZE) { - PrintAndLogEx(FAILED, "Filename too long"); - errors = true; - break; - } - cmdp += 2; - break; - case 's': - bytes = param_get32ex(Cmd, cmdp + 1, 256, 10); - if (bytes > 4096) { - PrintAndLogEx(WARNING, "Emulator memory is max 4096bytes. Truncating %u to 4096", bytes); - bytes = 4096; - } - cmdp += 2; - break; - default: - PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); - errors = true; - break; - } + + uint16_t bytes = arg_get_int_def(ctx, 2, 256); + + if (bytes > 4096) { + PrintAndLogEx(WARNING, "Emulator memory is max 4096bytes. Truncating %u to 4096", bytes); + bytes = 4096; } - //Validations - if (errors) { - return usage_hf_iclass_esave(); - } + CLIParserFree(ctx); uint8_t *dump = calloc(bytes, sizeof(uint8_t)); if (dump == NULL) { @@ -1017,7 +977,7 @@ static int CmdHFiClassESave(const char *Cmd) { } // user supplied filename? - if (len < 1) { + if (fnlen < 1) { fnameptr += snprintf(fnameptr, sizeof(filename), "hf-iclass-"); FillFileNameByUID(fnameptr, dump, "-dump", 8); } @@ -1032,44 +992,37 @@ static int CmdHFiClassESave(const char *Cmd) { } static int CmdHFiClassEView(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf iclass eview", + "Display emulator memory.\n" + "Number of bytes to download defaults to 256. Other value is 2048.", + "hf iclass eview\n" + "hf iclass eview -s 2048\n" + "hf iclass eview -s 2048 -v"); - uint16_t blocks = 32, bytes = 256; - bool errors = false, verbose = false; - uint8_t cmdp = 0; - while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { - switch (tolower(param_getchar(Cmd, cmdp))) { - case 'h': - return usage_hf_iclass_eview(); - case 's': - bytes = param_get32ex(Cmd, cmdp + 1, 256, 10); + void *argtable[] = { + arg_param_begin, + arg_int0("s", "size", "<256|2048>", "number of bytes to save (default 256)"), + arg_lit0("v", "verbose" , "filename of dumpfile"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); - if (bytes > 4096) { - PrintAndLogEx(WARNING, "Emulator memory is max 4096bytes. Truncating %u to 4096", bytes); - bytes = 4096; - } + uint16_t blocks = 32; + uint16_t bytes = arg_get_int_def(ctx, 1, 256); + bool verbose = arg_get_lit(ctx, 2); + blocks = bytes / 8; - if (bytes % 8 != 0) { - bytes &= 0xFFF8; - PrintAndLogEx(WARNING, "Number not divided by 8, truncating to %u", bytes); - } + CLIParserFree(ctx); - blocks = bytes / 8; - cmdp += 2; - break; - case 'v': - verbose = true; - cmdp++; - break; - default: - PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); - errors = true; - break; - } + if (bytes > 4096) { + PrintAndLogEx(WARNING, "Emulator memory is max 4096bytes. Truncating %u to 4096", bytes); + bytes = 4096; } - //Validations - if (errors || bytes == 0) { - return usage_hf_iclass_eview(); + if (bytes % 8 != 0) { + bytes &= 0xFFF8; + PrintAndLogEx(WARNING, "Number not divided by 8, truncating to %u", bytes); } uint8_t *dump = calloc(bytes, sizeof(uint8_t)); From 1afb335478ad34275fe9a6d566d5d9e70b68d240 Mon Sep 17 00:00:00 2001 From: tcprst Date: Sun, 22 Nov 2020 19:39:54 -0500 Subject: [PATCH 05/52] make style --- armsrc/iclass.c | 2 +- client/src/cmdhf14a.c | 24 ++++++------ client/src/cmdhficlass.c | 6 +-- client/src/cmdhfmfdes.c | 74 ++++++++++++++++++------------------ client/src/cmdlfem4x05.c | 2 +- client/src/cmdlft55xx.c | 18 ++++----- client/src/cmdmain.c | 6 +-- client/src/crypto/asn1dump.c | 4 +- client/src/emv/emv_tags.c | 26 ++++++------- client/src/fido/fidocore.c | 2 +- client/src/util.c | 14 +++---- 11 files changed, 89 insertions(+), 89 deletions(-) diff --git a/armsrc/iclass.c b/armsrc/iclass.c index 803972e05..86d8094b7 100644 --- a/armsrc/iclass.c +++ b/armsrc/iclass.c @@ -1853,7 +1853,7 @@ void iClass_WriteBlock(uint8_t *msg) { // new block data memcpy(write + 2, payload->data, 8); - + uint8_t pagemap = get_pagemap(&hdr); if (pagemap == PICOPASS_NON_SECURE_PAGEMODE) { // Unsecured tags uses CRC16, but don't include the UPDATE operation code diff --git a/client/src/cmdhf14a.c b/client/src/cmdhf14a.c index 852aae3b8..c8d0a57eb 100644 --- a/client/src/cmdhf14a.c +++ b/client/src/cmdhf14a.c @@ -1178,11 +1178,11 @@ static int CmdHF14AAPDU(const char *Cmd) { } CLIParserFree(ctx); - PrintAndLogEx(SUCCESS, "( " _YELLOW_("%s%s%s")" )", - activateField ? "select" : "", - leaveSignalON ? ", keep" : "", - decodeTLV ? ", TLV" : "" - ); + PrintAndLogEx(SUCCESS, "( " _YELLOW_("%s%s%s")" )", + activateField ? "select" : "", + leaveSignalON ? ", keep" : "", + decodeTLV ? ", TLV" : "" + ); PrintAndLogEx(SUCCESS, ">>> %s", sprint_hex_inrow(data, datalen)); if (decodeAPDU) { @@ -1217,7 +1217,7 @@ static int CmdHF14ACmdRaw(const char *Cmd) { "Sends an raw bytes over ISO14443a. With option to use TOPAZ 14a mode.", "hf 14a raw -sc 3000 -> select, crc, where 3000 == 'read block 00'\n" "hf 14a raw -ak -b 7 40 -> send 7 bit byte 0x40\n" - ); + ); void *argtable[] = { arg_param_begin, @@ -1344,13 +1344,13 @@ static int waitCmd(bool i_select, uint32_t timeout) { if (i_select == false && len >= 3) { bool crc = check_crc(CRC_14443_A, data, len); - + char s[16]; - sprintf(s, - (crc) ? _GREEN_("%02X %02X") : _RED_("%02X %02X"), - data[len - 2], - data[len - 1] - ); + sprintf(s, + (crc) ? _GREEN_("%02X %02X") : _RED_("%02X %02X"), + data[len - 2], + data[len - 1] + ); PrintAndLogEx(SUCCESS, "%s[ %s ]", sprint_hex(data, len - 2), s); } else { diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index c72db32b4..2c90278a7 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -1003,7 +1003,7 @@ static int CmdHFiClassEView(const char *Cmd) { void *argtable[] = { arg_param_begin, arg_int0("s", "size", "<256|2048>", "number of bytes to save (default 256)"), - arg_lit0("v", "verbose" , "filename of dumpfile"), + arg_lit0("v", "verbose", "filename of dumpfile"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -3546,8 +3546,8 @@ int info_iclass(void) { uint8_t cardtype = get_mem_config(hdr); PrintAndLogEx(SUCCESS, " Card type.... " _GREEN_("%s"), card_types[cardtype]); - - + + } DropField(); diff --git a/client/src/cmdhfmfdes.c b/client/src/cmdhfmfdes.c index a69aa0d27..f2282dbfd 100644 --- a/client/src/cmdhfmfdes.c +++ b/client/src/cmdhfmfdes.c @@ -1182,7 +1182,7 @@ static int mifare_desfire_change_key(uint8_t key_no, uint8_t *new_key, uint8_t n key 8b cpy 8b crc 2b - padding + padding */ // Variable length ciphered key data 24-42 bytes plus padding.. @@ -1190,11 +1190,11 @@ static int mifare_desfire_change_key(uint8_t key_no, uint8_t *new_key, uint8_t n sAPDU apdu = {0x90, MFDES_CHANGE_KEY, 0x00, 0x00, 0x01, data}; // 0xC4 size_t cmdcnt = 0; - + uint8_t new_key_length = 16; switch (new_algo) { case MFDES_ALGO_DES: - // double + // double memcpy(data + cmdcnt + 1, new_key, new_key_length); memcpy(data + cmdcnt + 1 + new_key_length, new_key, new_key_length); break; @@ -1438,11 +1438,11 @@ static int desfire_print_amk_keysetting(uint8_t key_settings, uint8_t num_keys, PrintAndLogEx(SUCCESS, " -- All keys (except AMK,see Bit0) within this application are frozen"); break; default: - PrintAndLogEx(SUCCESS, - " -- Authentication with the specified key is necessary to change any key.\n" - "A change key and a PICC master key (CMK) can only be changed after authentication with the master key.\n" - "For keys other then the master or change key, an authentication with the same key is needed." - ); + PrintAndLogEx(SUCCESS, + " -- Authentication with the specified key is necessary to change any key.\n" + "A change key and a PICC master key (CMK) can only be changed after authentication with the master key.\n" + "For keys other then the master or change key, an authentication with the same key is needed." + ); break; } @@ -1487,15 +1487,15 @@ static int desfire_print_piccmk_keysetting(uint8_t key_settings, uint8_t num_key if (res == PM3_SUCCESS) PrintAndLogEx(SUCCESS, " [0xAA] Authenticate AES : %s", (res == PM3_SUCCESS) ? _YELLOW_("YES") : "NO"); - PrintAndLogEx(INFO, "-------------------------------------------------------------"); - PrintAndLogEx(INFO, " Key setting: 0x%02X [%c%c%c%c]", - key_settings, - (key_settings & (1 << 3)) ? '1' : '0', - (key_settings & (1 << 2)) ? '1' : '0', - (key_settings & (1 << 1)) ? '1' : '0', - (key_settings & (1 << 0)) ? '1' : '0' - ); - + PrintAndLogEx(INFO, "-------------------------------------------------------------"); + PrintAndLogEx(INFO, " Key setting: 0x%02X [%c%c%c%c]", + key_settings, + (key_settings & (1 << 3)) ? '1' : '0', + (key_settings & (1 << 2)) ? '1' : '0', + (key_settings & (1 << 1)) ? '1' : '0', + (key_settings & (1 << 0)) ? '1' : '0' + ); + PrintAndLogEx(SUCCESS, " [1...] CMK Configuration changeable : %s", (key_settings & (1 << 3)) ? _GREEN_("YES") : "NO (frozen)"); PrintAndLogEx(SUCCESS, " [.1..] CMK required for create/delete : %s", (key_settings & (1 << 2)) ? _GREEN_("NO") : "YES"); PrintAndLogEx(SUCCESS, " [..1.] Directory list access with CMK : %s", (key_settings & (1 << 1)) ? _GREEN_("NO") : "YES"); @@ -1645,14 +1645,14 @@ static int handler_desfire_select_application(uint8_t *aid) { sAPDU apdu = {0x90, MFDES_SELECT_APPLICATION, 0x00, 0x00, 0x03, aid}; //0x5a uint32_t recv_len = 0; uint16_t sw = 0; - + int res = send_desfire_cmd(&apdu, !tag->rf_field_on, NULL, &recv_len, &sw, sizeof(dfname_t), true); if (res != PM3_SUCCESS) { PrintAndLogEx(WARNING, - _RED_(" Can't select AID 0x%X -> %s"), - (aid[2] << 16) + (aid[1] << 8) + aid[0], - GetErrorString(res, &sw) - ); + _RED_(" Can't select AID 0x%X -> %s"), + (aid[2] << 16) + (aid[1] << 8) + aid[0], + GetErrorString(res, &sw) + ); DropFieldDesfire(); return res; } @@ -1732,17 +1732,17 @@ static int handler_desfire_createapp(aidhdr_t *aidhdr, bool usename, bool usefid apdu.Lc = apdu.Lc - sizeof(aidhdr->fid); } uint8_t *data = NULL; - + // skip over FID if not used. if (usefid == false && usename) { data = calloc(apdu.Lc, sizeof(uint8_t)); apdu.data = data; - + memcpy(data, aidhdr->aid, sizeof(aidhdr->aid)); data[3] = aidhdr->keysetting1; data[4] = aidhdr->keysetting2; memcpy(data + 5, aidhdr->name, sizeof(aidhdr->name)); - + PrintAndLogEx(INFO, "new data: %s", sprint_hex_inrow(data, apdu.Lc)); } @@ -2070,7 +2070,7 @@ static int getKeySettings(uint8_t *aid) { uint8_t key_setting = 0; int res = 0; if (memcmp(aid, "\x00\x00\x00", 3) == 0) { - + // CARD MASTER KEY //PrintAndLogEx(INFO, "--- " _CYAN_("CMK - PICC, Card Master Key settings")); @@ -2418,7 +2418,7 @@ static int CmdHF14ADesCreateApp(const char *Cmd) { if (usefid) memcpy(aidhdr.fid, fid, sizeof(aidhdr.fid)); - if (usename) + if (usename) memcpy(aidhdr.name, name, sizeof(aidhdr.name)); PrintAndLogEx(INFO, "Creating AID using:"); @@ -2430,14 +2430,14 @@ static int CmdHF14ADesCreateApp(const char *Cmd) { if (usename) PrintAndLogEx(INFO, "DF Name %s", aidhdr.name); -/* - uint8_t rootaid[3] = {0x00, 0x00, 0x00}; - int res = handler_desfire_select_application(rootaid); - if (res != PM3_SUCCESS) { - DropFieldDesfire(); - return res; - } -*/ + /* + uint8_t rootaid[3] = {0x00, 0x00, 0x00}; + int res = handler_desfire_select_application(rootaid); + if (res != PM3_SUCCESS) { + DropFieldDesfire(); + return res; + } + */ int res = handler_desfire_createapp(&aidhdr, usename, usefid); DropFieldDesfire(); @@ -3462,7 +3462,7 @@ static int CmdHF14ADesInfo(const char *Cmd) { PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "--- " _CYAN_("Hardware Information")); PrintAndLogEx(INFO, " raw: %s", sprint_hex_inrow(info.versionHW, sizeof(info.versionHW))); - + PrintAndLogEx(INFO, " Vendor Id: " _YELLOW_("%s"), getTagInfo(info.versionHW[0])); PrintAndLogEx(INFO, " Type: " _YELLOW_("0x%02X"), info.versionHW[1]); PrintAndLogEx(INFO, " Subtype: " _YELLOW_("0x%02X"), info.versionHW[2]); @@ -3698,7 +3698,7 @@ static int CmdHF14ADesDump(const char *Cmd) { }; CLIExecWithReturn(ctx, Cmd, argtable, true); CLIParserFree(ctx); - + (void)Cmd; // Cmd is not used so far DropFieldDesfire(); diff --git a/client/src/cmdlfem4x05.c b/client/src/cmdlfem4x05.c index 9abc2862c..3b260ed2e 100644 --- a/client/src/cmdlfem4x05.c +++ b/client/src/cmdlfem4x05.c @@ -1882,7 +1882,7 @@ uint32_t static em4x05_Sniff_GetBlock(char *bits, bool fwd) { if (parity != (bits[35] - '0')) parityerror = true; - if (parityerror) + if (parityerror) PrintAndLogEx(ERR, "parity error : "); if (!fwd) { diff --git a/client/src/cmdlft55xx.c b/client/src/cmdlft55xx.c index a3ccf2052..80c8eda4f 100644 --- a/client/src/cmdlft55xx.c +++ b/client/src/cmdlft55xx.c @@ -1027,7 +1027,7 @@ static void T55xx_Print_DownlinkMode(uint8_t downlink_mode) { } // Define prototype to call from within detect. -static int CmdT55xxWakeUp (const char *Cmd); +static int CmdT55xxWakeUp(const char *Cmd); static int CmdT55xxDetect(const char *Cmd) { @@ -1054,7 +1054,7 @@ static int CmdT55xxDetect(const char *Cmd) { return usage_t55xx_detect(); case 'p': password = param_get32ex(Cmd, cmdp + 1, 0, 16); - sprintf (wakecmd,"p %08x q",(uint32_t)(password & 0xFFFFFFFF)); + sprintf(wakecmd, "p %08x q", (uint32_t)(password & 0xFFFFFFFF)); usepwd = true; cmdp += 2; break; @@ -1077,7 +1077,7 @@ static int CmdT55xxDetect(const char *Cmd) { } if (errors) return usage_t55xx_detect(); - + // detect called so clear data blocks T55x7_ClearAllBlockData(); @@ -1096,11 +1096,11 @@ static int CmdT55xxDetect(const char *Cmd) { if (usewake) { // call wake if (try_with_pwd) - CmdT55xxWakeUp (wakecmd); + CmdT55xxWakeUp(wakecmd); else - CmdT55xxWakeUp ("q"); + CmdT55xxWakeUp("q"); // sleep 90 ms - nanosleep (&sleepperiod, &sleepperiod); + nanosleep(&sleepperiod, &sleepperiod); } if (AcquireData(T55x7_PAGE0, T55x7_CONFIGURATION_BLOCK, (try_with_pwd && usepwd), password, m) == false) @@ -1117,11 +1117,11 @@ static int CmdT55xxDetect(const char *Cmd) { if (usewake) { // call wake if (try_with_pwd) - CmdT55xxWakeUp (wakecmd); + CmdT55xxWakeUp(wakecmd); else - CmdT55xxWakeUp ("q"); + CmdT55xxWakeUp("q"); // sleep 90 ms - nanosleep (&sleepperiod, &sleepperiod); + nanosleep(&sleepperiod, &sleepperiod); } if (AcquireData(T55x7_PAGE0, T55x7_CONFIGURATION_BLOCK, usepwd, password, downlink_mode)) { diff --git a/client/src/cmdmain.c b/client/src/cmdmain.c index 551f5ccf6..c55c7792e 100644 --- a/client/src/cmdmain.c +++ b/client/src/cmdmain.c @@ -120,7 +120,7 @@ static int lf_search_plus(const char *Cmd) { return retval; } -static int CmdAuto(const char *Cmd) { +static int CmdAuto(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "auto", "Run LF SEARCH / HF SEARCH / DATA PLOT / DATA SAVE", @@ -136,7 +136,7 @@ static int CmdAuto(const char *Cmd) { PrintAndLogEx(INFO, "lf search"); int ret = CmdLFfind(""); - if (ret == PM3_SUCCESS) + if (ret == PM3_SUCCESS) return ret; PrintAndLogEx(INFO, "hf search"); @@ -193,7 +193,7 @@ static int CmdHints(const char *Cmd) { PrintAndLogEx(ERR, "you can't turn off and on at the same time"); return PM3_EINVARG; } - + if (turn_off) { session.show_hints = false; } else if (turn_on) { diff --git a/client/src/crypto/asn1dump.c b/client/src/crypto/asn1dump.c index f1addb9e2..cae25c1b6 100644 --- a/client/src/crypto/asn1dump.c +++ b/client/src/crypto/asn1dump.c @@ -116,7 +116,7 @@ static void asn1_tag_dump_str_time(const struct tlv *tlv, const struct asn1_tag if (longyear == false) PrintAndLogEx(NORMAL, "20" NOLF); - PrintAndLogEx(NORMAL, "%s-" NOLF, sprint_hex(tlv->value, startindx) ); + PrintAndLogEx(NORMAL, "%s-" NOLF, sprint_hex(tlv->value, startindx)); if (len < startindx + 2) break; @@ -276,7 +276,7 @@ static void asn1_tag_dump_object_id(const struct tlv *tlv, const struct asn1_tag asn1_buf.p = (uint8_t *)tlv->value; char pstr[300]; mbedtls_oid_get_numeric_string(pstr, sizeof(pstr), &asn1_buf); - + PrintAndLogEx(INFO, "%*s %s" NOLF, (level * 4), " ", pstr); char *jsondesc = asn1_oid_description(pstr, true); diff --git a/client/src/emv/emv_tags.c b/client/src/emv/emv_tags.c index 661df0e5e..f86b88826 100644 --- a/client/src/emv/emv_tags.c +++ b/client/src/emv/emv_tags.c @@ -462,9 +462,9 @@ static void emv_tag_dump_bitmask(const struct tlv *tlv, const struct emv_tag *ta if (val & 0x80) { PrintAndLogEx(INFO, "%*s" NOLF, (level * 4), " "); PrintAndLogEx(NORMAL, " %s - '%s'", - bitstrings[bit - 1], - (bits->bit == EMV_BIT(byte, bit)) ? bits->name : "Unknown" - ); + bitstrings[bit - 1], + (bits->bit == EMV_BIT(byte, bit)) ? bits->name : "Unknown" + ); } if (bits->bit == EMV_BIT(byte, bit)) bits ++; @@ -535,10 +535,10 @@ static void emv_tag_dump_numeric(const struct tlv *tlv, const struct emv_tag *ta static void emv_tag_dump_yymmdd(const struct tlv *tlv, const struct emv_tag *tag, int level) { PrintAndLogEx(INFO, "%*s" NOLF, (level * 4), " "); PrintAndLogEx(NORMAL, " Date: 20%02lu.%lu.%lu", - emv_value_numeric(tlv, 0, 2), - emv_value_numeric(tlv, 2, 4), - emv_value_numeric(tlv, 4, 6) - ); + emv_value_numeric(tlv, 0, 2), + emv_value_numeric(tlv, 2, 4), + emv_value_numeric(tlv, 4, 6) + ); } static uint32_t emv_get_binary(const unsigned char *S) { @@ -749,12 +749,12 @@ static void emv_tag_dump_cvm_list(const struct tlv *tlv, const struct emv_tag *t PrintAndLogEx(INFO, "%*s" NOLF, (level * 4), " "); PrintAndLogEx(NORMAL, " %02x %02x: '%s' '%s' and '%s' if this CVM is unsuccessful", - tlv->value[i], - tlv->value[i + 1], - method, - condition, - (tlv->value[i] & 0x40) ? "continue" : "fail" - ); + tlv->value[i], + tlv->value[i + 1], + method, + condition, + (tlv->value[i] & 0x40) ? "continue" : "fail" + ); } } diff --git a/client/src/fido/fidocore.c b/client/src/fido/fidocore.c index abe3f28ce..fcebb2674 100644 --- a/client/src/fido/fidocore.c +++ b/client/src/fido/fidocore.c @@ -541,7 +541,7 @@ int FIDO2MakeCredentionalParseRes(json_t *root, uint8_t *data, size_t dataLen, b res = CborGetArrayBinStringValue(&mapsmt, der, sizeof(der), &derLen); cbor_check(res); if (verbose2) { - PrintAndLogEx(INFO, "DER certificate[%zu]:", derLen); + PrintAndLogEx(INFO, "DER certificate[%zu]:", derLen); PrintAndLogEx(INFO, "------------------DER-------------------"); PrintAndLogEx(INFO, "%s", sprint_hex(der, derLen)); PrintAndLogEx(INFO, "----------------DER---------------------"); diff --git a/client/src/util.c b/client/src/util.c index 2e1cfb0bd..76a2d7885 100644 --- a/client/src/util.c +++ b/client/src/util.c @@ -212,23 +212,23 @@ void print_buffer(const uint8_t *data, const size_t len, int level) { // (16 * 3) + (16) + + 1 memset(buf, 0, sizeof(buf)); sprintf(buf, "%*s%02x: ", (level * 4), " ", i); - - hex_to_buffer((uint8_t *)(buf + strlen(buf)), data + i, 16, (sizeof(buf) - strlen(buf) - 1), 0, 1, true); + + hex_to_buffer((uint8_t *)(buf + strlen(buf)), data + i, 16, (sizeof(buf) - strlen(buf) - 1), 0, 1, true); snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "| %s", sprint_ascii(data + i, 16)); PrintAndLogEx(INFO, "%s", buf); } - + // the last odd bytes uint8_t mod = len % 16; - + if (mod) { memset(buf, 0, sizeof(buf)); sprintf(buf, "%*s%02x: ", (level * 4), " ", i); hex_to_buffer((uint8_t *)(buf + strlen(buf)), data + i, mod, (sizeof(buf) - strlen(buf) - 1), 0, 1, true); - + // add the spaces... - snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "%*s", ((16 - mod) * 3) , " "); - + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "%*s", ((16 - mod) * 3), " "); + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "| %s", sprint_ascii(data + i, mod)); PrintAndLogEx(INFO, "%s", buf); } From 4d616a1c09bf740abbfc97d02a3929d6462f8ad0 Mon Sep 17 00:00:00 2001 From: tcprst Date: Mon, 23 Nov 2020 14:46:07 -0500 Subject: [PATCH 06/52] hf iclass decrypt - now use cliparser --- client/src/cmdhficlass.c | 148 ++++++++++++++++++--------------------- doc/cheatsheet.md | 11 +-- 2 files changed, 73 insertions(+), 86 deletions(-) diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index 2c90278a7..8a2cbf2d7 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -71,28 +71,6 @@ static int usage_hf_iclass_sim(void) { PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; } -static int usage_hf_iclass_decrypt(void) { - PrintAndLogEx(NORMAL, "3DES decrypt data\n"); - PrintAndLogEx(NORMAL, "This is naive implementation, it tries to decrypt every block after block 6."); - PrintAndLogEx(NORMAL, "Correct behaviour would be to decrypt only the application areas where the key is valid,"); - PrintAndLogEx(NORMAL, "which is defined by the configuration block."); - PrintAndLogEx(NORMAL, "OBS! In order to use this function, the file 'iclass_decryptionkey.bin' must reside"); - PrintAndLogEx(NORMAL, "in the resources directory. The file should be 16 bytes binary data\n"); - PrintAndLogEx(NORMAL, "Usage: hf iclass decrypt d f k \n"); - PrintAndLogEx(NORMAL, "Options"); - PrintAndLogEx(NORMAL, " h : Show this help"); - PrintAndLogEx(NORMAL, " d : 16 bytes hex"); - PrintAndLogEx(NORMAL, " f : filename of dump"); - PrintAndLogEx(NORMAL, " k : 16 bytes hex"); - PrintAndLogEx(NORMAL, " v : verbose output"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_("\thf iclass decrypt f hf-iclass-AA162D30F8FF12F1-dump.bin")); - PrintAndLogEx(NORMAL, _YELLOW_("\thf iclass decrypt f hf-iclass-AA162D30F8FF12F1-dump.bin k 000102030405060708090a0b0c0d0e0f")); - PrintAndLogEx(NORMAL, _YELLOW_("\thf iclass decrypt d 1122334455667788 k 000102030405060708090a0b0c0d0e0f")); - PrintAndLogEx(NORMAL, ""); - return PM3_SUCCESS; -} static int usage_hf_iclass_encrypt(void) { PrintAndLogEx(NORMAL, "3DES encrypt data\n"); PrintAndLogEx(NORMAL, "OBS! In order to use this function, the file " _YELLOW_("'iclass_decryptionkey.bin'") " must reside"); @@ -1003,7 +981,7 @@ static int CmdHFiClassEView(const char *Cmd) { void *argtable[] = { arg_param_begin, arg_int0("s", "size", "<256|2048>", "number of bytes to save (default 256)"), - arg_lit0("v", "verbose", "filename of dumpfile"), + arg_lit0("v", "verbose", "verbose output"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -1049,74 +1027,82 @@ static int CmdHFiClassEView(const char *Cmd) { free(dump); return PM3_SUCCESS; } - static int CmdHFiClassDecrypt(const char *Cmd) { + CLIParserContext *clictx; + CLIParserInit(&clictx, "hf iclass decrypt", + "3DES decrypt data\n" + "This is naive implementation, it tries to decrypt every block after block 6.\n" + "Correct behaviour would be to decrypt only the application areas where the key is valid,\n" + "which is defined by the configuration block.\n" + "OBS! In order to use this function, the file 'iclass_decryptionkey.bin' must reside\n" + "in the resources directory. The file should be 16 bytes binary data", + "hf iclass decrypt -f hf-iclass-AA162D30F8FF12F1-dump.bin\n" + "hf iclass decrypt -f hf-iclass-AA162D30F8FF12F1-dump.bin --key 000102030405060708090a0b0c0d0e0f\n" + "hf iclass decrypt -d 1122334455667788 --key 000102030405060708090a0b0c0d0e0f"); - bool errors = false; - bool have_key = false; - bool have_data = false; - bool have_file = false; - bool verbose = false; - uint8_t cmdp = 0; + void *argtable[] = { + arg_param_begin, + arg_str0("f", "file", "", "filename of dumpfile"), + arg_str0("d", "data", "", "3DES encrypted data"), + arg_str0(NULL, "key", "", "3DES transport key"), + arg_lit0("v", "verbose", "verbose output"), + arg_param_end + }; + CLIExecWithReturn(clictx, Cmd, argtable, false); - uint8_t enc_data[8] = {0}; - uint8_t dec_data[8] = {0}; - - size_t keylen = 0; - uint8_t key[32] = {0}; - uint8_t *keyptr = NULL; + int fnlen = 0; + char filename[FILE_PATH_SIZE] = {0}; + CLIParamStrToBuf(arg_get_str(clictx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); size_t decryptedlen = 0; uint8_t *decrypted = NULL; - char filename[FILE_PATH_SIZE]; + bool have_file = false; - while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { - switch (tolower(param_getchar(Cmd, cmdp))) { - case 'h': - return usage_hf_iclass_decrypt(); - case 'd': - if (param_gethex(Cmd, cmdp + 1, enc_data, 16)) { - PrintAndLogEx(ERR, "Data must be 16 HEX symbols"); - errors = true; - break; - } - have_data = true; - cmdp += 2; - break; - case 'f': - if (param_getstr(Cmd, cmdp + 1, filename, sizeof(filename)) == 0) { - PrintAndLogEx(WARNING, "No filename found after f"); - errors = true; - break; - } - - if (loadFile_safe(filename, "", (void **)&decrypted, &decryptedlen) != PM3_SUCCESS) { - errors = true; - break; - } - have_file = true; - cmdp += 2; - break; - case 'k': - if (param_gethex(Cmd, cmdp + 1, key, 32)) { - PrintAndLogEx(ERR, "Transport key must include 32 HEX symbols"); - errors = true; - } - have_key = true; - cmdp += 2; - break; - case 'v': - verbose = true; - cmdp++; - break; - default: - PrintAndLogEx(WARNING, "Unknown parameter '%c'\n", param_getchar(Cmd, cmdp)); - errors = true; - break; + if (fnlen > 0) { + if (loadFile_safe(filename, "", (void **)&decrypted, &decryptedlen) != PM3_SUCCESS) { + CLIParserFree(clictx); + return PM3_EINVARG; } + have_file = true; } - if (errors || cmdp < 1) return usage_hf_iclass_decrypt(); + int enc_data_len = 0; + uint8_t enc_data[8] = {0}; + bool have_data = false; + + CLIGetHexWithReturn(clictx, 2, enc_data, &enc_data_len); + + if (enc_data_len > 0) { + if (enc_data_len != 8) { + PrintAndLogEx(ERR, "Data must be 8 bytes (16 HEX characters)"); + CLIParserFree(clictx); + return PM3_EINVARG; + } + have_data = true; + } + + int key_len = 0; + uint8_t key[32] = {0}; + uint8_t *keyptr = NULL; + bool have_key = false; + + CLIGetHexWithReturn(clictx, 3, key, &key_len); + + if (key_len > 0) { + if (key_len != 16) { + PrintAndLogEx(ERR, "Transport key must be 16 bytes (32 HEX characters)"); + CLIParserFree(clictx); + return PM3_EINVARG; + } + have_key = true; + } + + bool verbose = arg_get_lit(clictx, 4); + + CLIParserFree(clictx); + + size_t keylen = 0; + uint8_t dec_data[8] = {0}; bool use_sc = IsCryptoHelperPresent(verbose); diff --git a/doc/cheatsheet.md b/doc/cheatsheet.md index a3ea9c579..86dc4a29e 100644 --- a/doc/cheatsheet.md +++ b/doc/cheatsheet.md @@ -121,12 +121,13 @@ Decrypt iCLASS Block / file ``` Options --- -d : 16 bytes hex -f : filename of dump -k : 16 bytes hex +-f, --file filename of dumpfile +-d, --data 3DES encrypted data + --key 3DES transport key +-v, --verbose verbose output -pm3 --> hf iclass decrypt d 2AD4C8211F996871 -pm3 --> hf iclass decrypt f hf-iclass-db883702f8ff12e0.bin +pm3 --> hf iclass decrypt -d 2AD4C8211F996871 +pm3 --> hf iclass decrypt -f hf-iclass-db883702f8ff12e0.bin ``` Load iCLASS dump into memory for simulation From 9ef0e07d49947945911b996eb33d5f4adb767a0b Mon Sep 17 00:00:00 2001 From: tcprst Date: Mon, 23 Nov 2020 15:42:32 -0500 Subject: [PATCH 07/52] hf iclass encrypt - now use cliparser --- client/src/cmdhficlass.c | 99 ++++++++++++++++++---------------------- doc/cheatsheet.md | 7 +-- 2 files changed, 49 insertions(+), 57 deletions(-) diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index 8a2cbf2d7..7a26cbb0c 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -71,23 +71,6 @@ static int usage_hf_iclass_sim(void) { PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; } -static int usage_hf_iclass_encrypt(void) { - PrintAndLogEx(NORMAL, "3DES encrypt data\n"); - PrintAndLogEx(NORMAL, "OBS! In order to use this function, the file " _YELLOW_("'iclass_decryptionkey.bin'") " must reside"); - PrintAndLogEx(NORMAL, "in the resources directory. The file should be 16 bytes binary data\n"); - PrintAndLogEx(NORMAL, "Usage: hf iclass encrypt d k \n"); - PrintAndLogEx(NORMAL, "Options"); - PrintAndLogEx(NORMAL, " h : Show this help"); - PrintAndLogEx(NORMAL, " d : 16 bytes hex"); - PrintAndLogEx(NORMAL, " k : 16 bytes hex"); - PrintAndLogEx(NORMAL, " v : verbose output"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_("\thf iclass encrypt d 0102030405060708")); - PrintAndLogEx(NORMAL, _YELLOW_("\thf iclass encrypt d 0102030405060708 k 00112233445566778899AABBCCDDEEFF")); - PrintAndLogEx(NORMAL, ""); - return PM3_SUCCESS; -} static int usage_hf_iclass_dump(void) { PrintAndLogEx(NORMAL, "Dump all memory from a iCLASS tag\n"); PrintAndLogEx(NORMAL, "Usage: hf iclass dump f k c [e|r|v]\n"); @@ -1082,7 +1065,7 @@ static int CmdHFiClassDecrypt(const char *Cmd) { } int key_len = 0; - uint8_t key[32] = {0}; + uint8_t key[16] = {0}; uint8_t *keyptr = NULL; bool have_key = false; @@ -1269,45 +1252,53 @@ static void iclass_encrypt_block_data(uint8_t *blk_data, uint8_t *key) { } static int CmdHFiClassEncryptBlk(const char *Cmd) { - bool errors = false; - bool have_key = false; - bool verbose = false; - uint8_t blk_data[8] = {0}; - uint8_t key[16] = {0}; - uint8_t *keyptr = NULL; - uint8_t cmdp = 0; + CLIParserContext *clictx; + CLIParserInit(&clictx, "hf iclass encrypt", + "3DES encrypt data\n" + "OBS! In order to use this function, the file 'iclass_decryptionkey.bin' must reside\n" + "in the resources directory. The file should be 16 bytes binary data", + "hf iclass encrypt -d 0102030405060708\n" + "hf iclass encrypt -d 0102030405060708 --key 00112233445566778899AABBCCDDEEFF"); - while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { - switch (tolower(param_getchar(Cmd, cmdp))) { - case 'h': - return usage_hf_iclass_encrypt(); - case 'd': - if (param_gethex(Cmd, cmdp + 1, blk_data, 16)) { - PrintAndLogEx(ERR, "Block data must include 16 HEX symbols"); - errors = true; - } - cmdp += 2; - break; - case 'k': - if (param_gethex(Cmd, cmdp + 1, key, 32)) { - PrintAndLogEx(ERR, "Transport key must include 32 HEX symbols"); - errors = true; - } - have_key = true; - cmdp += 2; - break; - case 'v': - verbose = true; - cmdp++; - break; - default: - PrintAndLogEx(WARNING, "Unknown parameter '%c'\n", param_getchar(Cmd, cmdp)); - errors = true; - break; - } + void *argtable[] = { + arg_param_begin, + arg_str1("d", "data", "", "data to encrypt"), + arg_str0(NULL, "key", "", "3DES transport key"), + arg_lit0("v", "verbose", "verbose output"), + arg_param_end + }; + CLIExecWithReturn(clictx, Cmd, argtable, false); + + int blk_data_len = 0; + uint8_t blk_data[8] = {0}; + + CLIGetHexWithReturn(clictx, 1, blk_data, &blk_data_len); + + if (blk_data_len != 8) { + PrintAndLogEx(ERR, "Block data must be 8 bytes (16 HEX characters)"); + CLIParserFree(clictx); + return PM3_EINVARG; } - if (errors || cmdp < 1) return usage_hf_iclass_encrypt(); + int key_len = 0; + uint8_t key[16] = {0}; + uint8_t *keyptr = NULL; + bool have_key = false; + + CLIGetHexWithReturn(clictx, 2, key, &key_len); + + if (key_len > 0) { + if (key_len != 16) { + PrintAndLogEx(ERR, "Transport key must be 16 bytes (32 HEX characters)"); + CLIParserFree(clictx); + return PM3_EINVARG; + } + have_key = true; + } + + bool verbose = arg_get_lit(clictx, 3); + + CLIParserFree(clictx); bool use_sc = IsCryptoHelperPresent(verbose); diff --git a/doc/cheatsheet.md b/doc/cheatsheet.md index 86dc4a29e..5de15e198 100644 --- a/doc/cheatsheet.md +++ b/doc/cheatsheet.md @@ -111,10 +111,11 @@ Encrypt iCLASS Block ``` Options --- -d : 16 bytes hex -k : 16 bytes hex +-d, --data data to encrypt + --key 3DES transport key +-v, --verbose verbose output -pm3 --> hf iclass encrypt d 0000000f2aa3dba8 +pm3 --> hf iclass encrypt -d 0000000f2aa3dba8 ``` Decrypt iCLASS Block / file From 0c0411c144685a0fbfbd8aea7b6afee86b8317fb Mon Sep 17 00:00:00 2001 From: tcprst Date: Mon, 23 Nov 2020 16:07:03 -0500 Subject: [PATCH 08/52] add short option and fix typo --- client/src/cmdhficlass.c | 12 ++++++------ doc/cheatsheet.md | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index 7a26cbb0c..284317a47 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -1014,20 +1014,20 @@ static int CmdHFiClassDecrypt(const char *Cmd) { CLIParserContext *clictx; CLIParserInit(&clictx, "hf iclass decrypt", "3DES decrypt data\n" - "This is naive implementation, it tries to decrypt every block after block 6.\n" + "This is a naive implementation, it tries to decrypt every block after block 6.\n" "Correct behaviour would be to decrypt only the application areas where the key is valid,\n" "which is defined by the configuration block.\n" "OBS! In order to use this function, the file 'iclass_decryptionkey.bin' must reside\n" "in the resources directory. The file should be 16 bytes binary data", "hf iclass decrypt -f hf-iclass-AA162D30F8FF12F1-dump.bin\n" - "hf iclass decrypt -f hf-iclass-AA162D30F8FF12F1-dump.bin --key 000102030405060708090a0b0c0d0e0f\n" - "hf iclass decrypt -d 1122334455667788 --key 000102030405060708090a0b0c0d0e0f"); + "hf iclass decrypt -f hf-iclass-AA162D30F8FF12F1-dump.bin -k 000102030405060708090a0b0c0d0e0f\n" + "hf iclass decrypt -d 1122334455667788 -k 000102030405060708090a0b0c0d0e0f"); void *argtable[] = { arg_param_begin, arg_str0("f", "file", "", "filename of dumpfile"), arg_str0("d", "data", "", "3DES encrypted data"), - arg_str0(NULL, "key", "", "3DES transport key"), + arg_str0("k", "key", "", "3DES transport key"), arg_lit0("v", "verbose", "verbose output"), arg_param_end }; @@ -1258,12 +1258,12 @@ static int CmdHFiClassEncryptBlk(const char *Cmd) { "OBS! In order to use this function, the file 'iclass_decryptionkey.bin' must reside\n" "in the resources directory. The file should be 16 bytes binary data", "hf iclass encrypt -d 0102030405060708\n" - "hf iclass encrypt -d 0102030405060708 --key 00112233445566778899AABBCCDDEEFF"); + "hf iclass encrypt -d 0102030405060708 -k 00112233445566778899AABBCCDDEEFF"); void *argtable[] = { arg_param_begin, arg_str1("d", "data", "", "data to encrypt"), - arg_str0(NULL, "key", "", "3DES transport key"), + arg_str0("k", "key", "", "3DES transport key"), arg_lit0("v", "verbose", "verbose output"), arg_param_end }; diff --git a/doc/cheatsheet.md b/doc/cheatsheet.md index 5de15e198..9699fabf0 100644 --- a/doc/cheatsheet.md +++ b/doc/cheatsheet.md @@ -112,7 +112,7 @@ Encrypt iCLASS Block Options --- -d, --data data to encrypt - --key 3DES transport key +-k, --key 3DES transport key -v, --verbose verbose output pm3 --> hf iclass encrypt -d 0000000f2aa3dba8 @@ -124,7 +124,7 @@ Options --- -f, --file filename of dumpfile -d, --data 3DES encrypted data - --key 3DES transport key +-k, --key 3DES transport key -v, --verbose verbose output pm3 --> hf iclass decrypt -d 2AD4C8211F996871 From 6200fbc55e6c1eb336ac9e5da6ce4ea41adea17b Mon Sep 17 00:00:00 2001 From: mwalker33 <51802811+mwalker33@users.noreply.github.com> Date: Tue, 24 Nov 2020 17:25:46 +1100 Subject: [PATCH 09/52] T55xx Guide --- doc/T5577_Guide.md | 694 +++++++++++++++++++++++++++++++++++++++ doc/t55xx_block0.png | Bin 0 -> 26953 bytes doc/t55xx_clock0_cfg.png | Bin 0 -> 62447 bytes doc/t55xx_mem_map.png | Bin 0 -> 44034 bytes 4 files changed, 694 insertions(+) create mode 100644 doc/T5577_Guide.md create mode 100644 doc/t55xx_block0.png create mode 100644 doc/t55xx_clock0_cfg.png create mode 100644 doc/t55xx_mem_map.png diff --git a/doc/T5577_Guide.md b/doc/T5577_Guide.md new file mode 100644 index 000000000..cdffadd20 --- /dev/null +++ b/doc/T5577_Guide.md @@ -0,0 +1,694 @@ +# T5577 Introduction Guide + +### Based on RRG proxmark3 fork. + +### Ver.1 8 Sep 2019 + +| Contents | +| ----------------------------------------------------------------------------------- | +| [Part 1](#part-1) | +| [Introduction](#introduction) | +| [T5577 Overview](#t5577-overview) | +| [What data is on my T5577](#what-data-is-on-my-t5577) | +| [Read and Write Blocks of Data](#read-and-write-blocks-of-data) | +| [Exercise 1](#exercise-1) | +| [How do I use a password](#how-do-i-use-a-password) | +| | +| [Part 2 – Configuration Blocks](#part-2-configuration-blocks) | +| [The configuration Block – Block 0 Page 0](#the-configuration-block-block-0-page-0) | +| [Exercise 2](#exercise-2) | +| [The configuration Block – Block 3 Page 1](#the-configuration-block-block-3-page-1) | + +# Part 1 + +## Introduction + +The T5577 is a generic LF (Low Frequency) RFID card the is used in the +125 Khz frequency space. It is a good card to use to learn about RFID and +learn how to use the proxmark3. + +It is highly recommend that when learning about RFID that learning how +to read the data sheets be near the top of the list. It can be very hard +as the data sheet will hold the information you need, but you don’t yet +know what it means. As such, I will attempt to point to sections of the +data sheet and would highly advise that you look at the data sheet as +you go. Overtime the data sheet may change, as a result things may not +always be reference correctly. + +As at writing this guide, the data sheet can be found at : + + + +This guide is not a how do I clone document. It is meant to help people +learn how to use the T5577 and in the process learn about rfid and the +proxmark3. + +Throughout this guide I will give examples. It is recommended that you +try these as we go. To do so, have a blank T5577 card that you can use +for this purpose. + +## T5577 Overview + +The T5577 is a chip that can hold data and a configuration (Section +4.12). + +In the diagram below, all white blocks can hold data. Some can be used +for a second purpose, such as the ‘password’ and ‘traceability data’. +The ‘Configuration Data’ and ‘Analog front end option setup’ will tell +the chip how to behave. + +![](./t55xx_mem_map.png) + + + +## What data is on my T5577 + +Let’s have a look and see what a card might look in the proxmark3 +software. Since we can change the configuration of how the T5577 will +output data, the proxmark3 software need to work out how to interpreted +the data it receives, we do this with the following command. + +It should be noted that the T5577 has many clones. As such the default +setup of each card may be different. If the tractability data is +present, then this will vary based on the batch of cards. + +Always run this command when you place a t5577 on the proxmark3. In all +examples shown, it will be assumed you have run the detect command. +``` +[usb] pm3 --> lf t55xx detect +``` +You should see a results simular to the following: +``` + Chip Type : T55x7 + Modulation : ASK + Bit Rate : 2 - RF/32 + Inverted : No + Offset : 32 + Seq. Term. : Yes + Block0 : 0x000880E0 + Downlink Mode : default/fixed bit length +``` +Now that the proxmark3 has detected a T55x7 chip, and found some +information about it, we should be able to see all the data on the chip. +``` +[usb] pm3 --> lf t55xx dump +``` +Your results should look similar to the following: +``` +[+] Reading Page 0: +[+] blk | hex data | binary | ascii +[+] ----+----------+----------------------------------+------- +[+] 00 | 000880E0 | 00000000000010001000000011100000 | .... +[+] 01 | FFFFFFFF | 11111111111111111111111111111111 | .... +[+] 02 | FFFFFFFF | 11111111111111111111111111111111 | .... +[+] 03 | FFFFFFFF | 11111111111111111111111111111111 | .... +[+] 04 | FFFFFFFF | 11111111111111111111111111111111 | .... +[+] 05 | FFFFFFFF | 11111111111111111111111111111111 | .... +[+] 06 | FFFFFFFF | 11111111111111111111111111111111 | .... +[+] 07 | FFFFFFFF | 11111111111111111111111111111111 | .... +[+] Reading Page 1: +[+] blk | hex data | binary | ascii +[+] ----+----------+----------------------------------+------- +[+] 00 | 000880E0 | 00000000000010001000000011100000 | .... +[+] 01 | E0150A48 | 11100000000101010000101001001000 | ...H +[+] 02 | 2D782308 | 00101101011110000010001100001000 | -x#. +[+] 03 | FFFFFFFF | 11111111111111111111111111111111 | .... +``` +I will cover the meaning of this data as we go, but for now, lets keep +it simple. + +## Read and Write Blocks of Data + +The basic function of using the proxmark3 with rfid cards is to read and +write data. This reading and writing must be done in the correct way +needed for the chip (and its configuration). Lucky for us, the +developers have done a great job and gave us commands. What we need to +know is that with the T5577 data is read/written one complete block at a +time. Each block holds 32 bits of data (hence the binary output shown) + +Since we know that the card has data and configuration blocks, lets say +away from those while we learn how to read and write. I suggest you +follow along and perform each command and check the results as we go. + +We can store our own data in blocks 1-7 (remember that block 7 will be +needed if we want to set a password). + +(Don’t forget to run the detect command: lf t55xx detect, and ensure you +can see the card) + +1) Check what is stored in block 1. The following command can be read + as, run a low frequency (lf) command for the T55xx chip (t55xx) and + read block (b) number 1. + ``` + [usb] pm3 --> lf t55xx read b 1 + ``` + result: + ``` + [+] Reading Page 0: + [+] blk | hex data | binary | ascii + [+] ----+----------+----------------------------------+------- + [+] 01 | FFFFFFFF | 11111111111111111111111111111111 | .... + ``` + Note: Depending on the history of your card your data may vary, but + should match the dump data. + +2) Write some new data into block 1 on the card. + + We use the d option to supply the data ‘12345678’ + ``` + [usb] pm3 --> lf t55xx write b 1 d 12345678 + ``` + result: + ``` + [=] Writing page 0 block: 01 data: 0x12345678 + ``` +3) Now, lets check if the data was written. + ``` + [usb] pm3 --> lf t55xx read b 1 + ``` + result: + ``` + [+] Reading Page 0: + [+] blk | hex data | binary | ascii + [+] ----+----------+----------------------------------+------- + [+] 01 | 12345678 | 00010010001101000101011001111000 | .4Vx + ``` +4) The data is written in Hexadecimal. A single hex digit holds 4 bits + of data. So to store 32 bits in a block we need to supply 8 hex + digits (8 \* 4 = 32). If you are familiar with hex and binary do a + little bit of home work to learn. The following is a quick start. + + | Hex | Binary | Decimal | + |:---:|:------:|:-------:| + | 0 | 0000 | 0 | + | 1 | 0001 | 1 | + | 2 | 0010 | 2 | + | 3 | 0011 | 3 | + | 4 | 0100 | 4 | + | 5 | 0101 | 5 | + | 6 | 0110 | 6 | + | 7 | 0111 | 7 | + | 8 | 1000 | 8 | + | 9 | 1001 | 9 | + | A | 1010 | 10 | + | B | 1011 | 11 | + | C | 1100 | 12 | + | D | 1101 | 13 | + | E | 1110 | 14 | + | F | 1111 | 15 | + + To use all the bits we supply the data in Hex format and it will + always be 8 hex digits. + + Lets try and write 89ABCDEF + ``` + [usb] pm3 --> lf t55xx write b 1 d 89abcdef + ``` + result: + ``` + [=] Writing page 0 block: 01 data: 0x89ABCDEF + ``` + and check + ``` + [usb] pm3 --> lf t55xx read b 1 + ``` + result: + ``` + [+] Reading Page 0: + [+] blk | hex data | binary | ascii + [+] ----+----------+----------------------------------+------- + [+] 01 | 89ABCDEF | 10001001101010111100110111101111 | .... + ``` + +### Exercise 1 + +Using the read and write commands you have learnt see if you can make +the lf t55 dump command show the following data for blocks 1-7 (Page 0). +Do not write to block 0 or try and change the data on page 1. +``` +[usb] pm3 --> lf t55 dump +``` +result: +``` +[+] Reading Page 0: +[+] blk | hex data | binary | ascii +[+] ----+----------+----------------------------------+------- +[+] 00 | 000880E0 | 00000000000010001000000011100000 | .... +[+] 01 | 89ABCDEF | 10001001101010111100110111101111 | .... +[+] 02 | FFFFFFFF | 11111111111111111111111111111111 | .... +[+] 03 | FFFFFFFF | 11111111111111111111111111111111 | .... +[+] 04 | FFFFFFFF | 11111111111111111111111111111111 | .... +[+] 05 | FFFFFFFF | 11111111111111111111111111111111 | .... +[+] 06 | FFFFFFFF | 11111111111111111111111111111111 | .... +[+] 07 | FFFFFFFF | 11111111111111111111111111111111 | .... +[+] Reading Page 1: +[+] blk | hex data | binary | ascii +[+] ----+----------+----------------------------------+------- +[+] 00 | 000880E0 | 00000000000010001000000011100000 | .... +[+] 01 | E0150A48 | 11100000000101010000101001001000 | ...H +[+] 02 | 2D782308 | 00101101011110000010001100001000 | -x#. +[+] 03 | FFFFFFFF | 11111111111111111111111111111111 | .... +``` + +Practice reading and writing to blocks 1 to 7 until you are happy you +can do it and get the results you wanted (i.e. the data you want stored +is written to the block you want it stored in). + +## How do I use a password + +This can be a little tricky for beginners. +***If you forget your password you will lose access to your card***. + +To tell the T5577 to use a password we have to change the data in the +configuration block (0). To help learn this and make it as simple as I +can, please read and follow exactly. If your results DON’T match 100% as +required, please do not proceed. + +1) Lets start with a known card state and wipe the card. This will set + a default configuration to block 0 and set all the data in blocks + 1-7 to a default. + ``` + [usb] pm3 --> lf t55xx wipe + ``` + Result: + ``` + [=] Begin wiping T55x7 tag + + [=] Default configation block 000880E0 + [=] Writing page 0 block: 00 data: 0x000880E0 + [=] Writing page 0 block: 01 data: 0x00000000 + [=] Writing page 0 block: 02 data: 0x00000000 + [=] Writing page 0 block: 03 data: 0x00000000 + [=] Writing page 0 block: 04 data: 0x00000000 + [=] Writing page 0 block: 05 data: 0x00000000 + [=] Writing page 0 block: 06 data: 0x00000000 + [=] Writing page 0 block: 07 data: 0x00000000 + ``` + +2) Check that the card is in the desired state. + ``` + [usb] pm3 --> lf t55xx detect + ``` + result: + ``` + Chip Type : T55x7 + Modulation : ASK + Bit Rate : 2 - RF/32 + Inverted : No + Offset : 32 + Seq. Term. : Yes + Block0 : 0x000880E0 + Downlink Mode : default/fixed bit length + ``` + + If block 0 does not hold the hex data **0x00088040 resolve this + first before proceeding.** + +3) Set the password we want to use. For this example lets use the + password : ***12345678*** + + The password is saved in block 7 of page 0. + ``` + [usb] pm3 --> lf t55xx write b 7 d 12345678 + ``` + result: + ``` + [=] Writing page 0 block: 07 data: 0x12345678 + ``` + +4) Lets verify both block 0 and block 7 + ``` + [usb] pm3 --> lf t55xx dump + ``` + result: + ``` + [+] Reading Page 0: + [+] blk | hex data | binary | ascii + [+] ----+----------+----------------------------------+------- + [+] 00 | 000880E0 | 00000000000010001000000011100000 | .... + [+] 01 | FFFFFFFF | 11111111111111111111111111111111 | .... + [+] 02 | FFFFFFFF | 11111111111111111111111111111111 | .... + [+] 03 | FFFFFFFF | 11111111111111111111111111111111 | .... + [+] 04 | FFFFFFFF | 11111111111111111111111111111111 | .... + [+] 05 | FFFFFFFF | 11111111111111111111111111111111 | .... + [+] 06 | FFFFFFFF | 11111111111111111111111111111111 | .... + [+] 07 | 12345678 | 00010010001101000101011001111000 | .4Vx + [+] Reading Page 1: + [+] blk | hex data | binary | ascii + [+] ----+----------+----------------------------------+------- + [+] 00 | 000880E0 | 00000000000010001000000011100000 | .... + [+] 01 | E0150A48 | 11100000000101010000101001001000 | ...H + [+] 02 | 2D782308 | 00101101011110000010001100001000 | -x#. + [+] 03 | FFFFFFFF | 11111111111111111111111111111111 | .... + ``` + ***Important : If block 0 and block 7 don’t match exactly, do not continue.*** + +5) Now we have a known configuration block and a known password of + 12345678, we are ready to tell the card to use the password. + + To do this the datasheet tells us we need to set the 28th + bit “PWD”. Check your datasheet and see the entire table (remember + the data sheet is your friend). + + ![](./t55xx_block0.png) + + We will cover other things in the configuration later. But the key + note here is we ONLY want to change bit 28 and nothing else. + + Current Block 0 : ***00088040*** + New Block 0 : ***00088050*** + + To understand what happened to get from 00088040 to 00088050 we need + to look at the binary data. + + While this can be confusing it is important to understand this as we + do more advanced things. + + Bit Location (28) + 000000000011111111112222222 ***2*** 2233 + 123456789012345678901234567 ***8*** 9012 + + | Hex Data | Binary Data | + |:--------:|:---------------------------------------| + | 00088040 | 000000000000100010000000010***0***0000 | + | 00088050 | 000000000000100010000000010***1***0000 | + + + + See how in the above we change the bit in location 28 from a 0 to 1 + 0 = No Password, 1 = Use Password + + Note how we did NOT change any other part of the configuration, only bit 28. + + To re-cap. + We put the card into a known configuration Block 0 : 00088040 + We set the a known password Block 7 : 12345678 + We altered the config data to tell the T5577 to use the password. + New Block 0 : 00088050 + + If you have completed all steps and have the exact same results, we are + ready to apply the new configuration. + ``` + [usb] pm3 --> lf t55xx write b 0 d 00088050 + ``` + result: + ``` + [=] Writing page 0 block: 00 data: 0x00088050 + ``` + +6) Lets check what happens when the password is set. + ``` + [usb] pm3 --> lf t55 detect + ``` + result: + ``` + [!] Could not detect modulation automatically. Try setting it manually with 'lf t55xx config' + ``` + Note how the lf t55 detect no longer seems to work\! + + In this case, this is due to needing a password to read/write to the + card. + + Lets try again, but this time supply the password. We use the option + p followed by the password. + ``` + [usb] pm3 --> lf t55 detect p 12345678 + ``` + result: + ``` + Chip Type : T55x7 + Modulation : ASK + Bit Rate : 2 - RF/32 + Inverted : No + Offset : 32 + Seq. Term. : Yes + Block0 : 0x00088050 + Downlink Mode : default/fixed bit length + ``` + +7) Write a block of data with a password + ``` + [usb] pm3 --> lf t55xx write b 1 d 1234abcd p 12345678 + ``` + result: + ``` + [=] Writing page 0 block: 01 data: 0x1234ABCD pwd: 0x12345678 + ``` + +8) Read a block of data with a password + + ***\*\*\*\* Important \*\*\*\**** + + ***Reading a T5577 block with a password when a password is not + enabled can result in locking the card. Please only use read with a + password when it is known that a password is in use.*** + + The proxmark3 has a safety check\! + ``` + [usb] pm3 --> lf t55xx read b 1 p 12345678 + ``` + result: + ``` + [+] Reading Page 0: + [+] blk | hex data | binary | ascii + [+] ----+----------+----------------------------------+------- + [!] Safety check: Could not detect if PWD bit is set in config block. Exits. + ``` + + Note that the proxmark3 did not read the block, the safty kicked in + and wants us to confirm by supply the override option ‘o’. + + Lets try again with the ‘o’ option as we know the password is set. + ``` + [usb] pm3 --> lf t55xx read b 1 p 12345678 o + ``` + result: + ``` + [+] Reading Page 0: + [+] blk | hex data | binary | ascii + [+] ----+----------+----------------------------------+------- + [=] Safety check overridden - proceeding despite risk + [+] 01 | 1234ABCD | 00010010001101001010101111001101 | .4.. + ``` + This time, we can see the data we wrote to block 1 is found with the + read command. + +9) Remove the need to supply the password. + + To do this we need to clear Bit 28 (set to 0) in the config. We have + this from above. + + Remember if we don’t know the config and write this config to the + card, it will over write all other settings. This can recoved the + card, but will lose any settings you may want. So it’s a good idea + to read the config, and set bit 28 to 0, rather than just overwrite + the config and change the way the card works. + + In our examples we know what it should be : 00088040 + ``` + [usb] pm3 --> lf t55xx write b 0 d 00088040 p 12345678 + ``` + result: + ``` + [=] Writing page 0 block: 00 data: 0x00088040 pwd: 0x12345678 + ``` + Now check if we can detect without a password + ``` + [usb] pm3 --> lf t55 detect + ``` + result: + ``` + Chip Type : T55x7 + Modulation : ASK + Bit Rate : 2 - RF/32 + Inverted : No + Offset : 32 + Seq. Term. : Yes + Block0 : 0x00088040 + Downlink Mode : default/fixed bit length + ``` + Yes we can and we can see Block 0 is the correct config 00088040 + +# Part 2 – Configuration Blocks + +One of the things a lot of people have trouble with or miss, is that the +T5577 has two different and separate communications protocols, each with +their own sub-protocols. + + - Card to Reader + - Reader to Card + +In Card to Reader, the T5577 will encode its data using the settings +from Block 0 in Page 0. It will use this in both default read mode +(where is sends out the blocks from 1 to x on power up), as well as when +it responds to commands. + +In the Read To Card, the T5577 will encode the data using the settings +from Block 3 Page 1. If the command is not encoded correctly it will +ignore the command and revert back to default read mode. + +## The configuration Block – Block 0 Page 0 + +For this configuration the settings chosen will be for the purpose of +the card when used in production. E.G. If you want the card to act like +an EM4100, then we need to choose the settings that work like the +EM4100; same goes for others like HID. I am not going to cover these +here, rather use an example. Others have collect these and posted on the +forum. + +To get started lets look back at the tech sheet. + +![](./t55xx_clock0_cfg.png) + +The non-password protect EM4100 could have a block 0 config of 00148040, +so what does it mean. + +To decode this config, we need to look at it in binary +00000000000101001000000001000000. Note that it had 32 bits and the +config block 0 is 32 bits. Now we can break it down. + +| Bits | Purpose | Value | +| ------- | ---------------------- | ----------- | +| 0000 | Master Key | Nothing Set | +| 0000000 | Not used in Basic Mode | | +| 101 | Data Bit Rate | RF/64 | +| 0 | Not used in Basic Mode | | +| 01000 | Modulation | Manchester | +| 00 | PSKCF | RF/2 | +| 0 | AOR | Not Set | +| 0 | Not used in Basic Mode | | +| 010 | Max Block | 2 | +| 0 | Password | Not Set | +| 0 | ST Sequence Terminator | Not Set | +| 00 | Not used in Basic Mode | | +| 0 | Init Delay | Not Set | + +To get more detail on each item, read through the data sheet. + +Lets see how the proxmark3 can help us learn. We will assume the T5577 +is in the same state from Part 1, where we can write to the card with no +password set (if not, review and get you card back to this state). + +1) Lets turn you T5577 into an EM4100 with ID 1122334455 + ``` + [usb] pm3 --> lf em 410x_write 1122334455 1 + ``` + result: + ``` + [+] Writing T55x7 tag with UID 0x1122334455 (clock rate: 64) + #db# Started writing T55x7 tag ... + #db# Clock rate: 64 + #db# Tag T55x7 written with 0xff8c65298c94a940 + ``` + +2) Check this has work. + ``` + [usb] pm3 --> lf search + ``` + result: + ``` + [=] NOTE: some demods output possible binary + [=] if it finds something that looks like a tag + [=] False Positives ARE possible + [=] + [=] Checking for known tags... + + [+] EM410x pattern found + + EM TAG ID : 1122334455 + + Possible de-scramble patterns + + Unique TAG ID : 8844CC22AA + HoneyWell IdentKey { + DEZ 8 : 03359829 + DEZ 10 : 0573785173 + DEZ 5.5 : 08755.17493 + DEZ 3.5A : 017.17493 + DEZ 3.5B : 034.17493 + DEZ 3.5C : 051.17493 + DEZ 14/IK2 : 00073588229205 + DEZ 15/IK3 : 000585269781162 + DEZ 20/ZK : 08080404121202021010 + } + Other : 17493_051_03359829 + Pattern Paxton : 289899093 [0x11478255] + Pattern 1 : 5931804 [0x5A831C] + Pattern Sebury : 17493 51 3359829 [0x4455 0x33 0x334455] + + [+] Valid EM410x ID found! + + + [+] Chipset detection : T55xx found + + [+] Try `lf t55xx` commands + ``` + Looks good. + +3) Now lest see what the T5577 detect and info shows + ``` + [usb] pm3 --> lf t55 detect + ``` + result: + ``` + [usb] pm3 --> lf t55 detect + Chip Type : T55x7 + Modulation : ASK + Bit Rate : 5 - RF/64 + Inverted : No + Offset : 32 + Seq. Term. : Yes + Block0 : 0x00148040 + Downlink Mode : default/fixed bit length + ``` + ``` + [usb] pm3 --> lf t55xx info + ``` + result: + ``` + + -- T55x7 Configuration & Tag Information -------------------- + ------------------------------------------------------------- + Safer key : 0 + reserved : 0 + Data bit rate : 5 - RF/64 + eXtended mode : No + Modulation : 8 - Manchester + PSK clock frequency : 0 - RF/2 + AOR - Answer on Request : No + OTP - One Time Pad : No + Max block : 2 + Password mode : No + Sequence Terminator : No + Fast Write : No + Inverse data : No + POR-Delay : No + ------------------------------------------------------------- + Raw Data - Page 0 + Block 0 : 0x00148040 00000000000101001000000001000000 + + Config block match : EM unique, Paxton + ------------------------------------------------------------- + ``` + We can see that the info gave us more information and confirmed what + we decoded by hand. But remember, the detect is still needed so the + proxmark3 software will know how to decode the info block. + + We can see that for the EM4100 emulation we have two blocks of data + (Max Block = 2). On the T5577 these will be Blocks 1 and 2. + +## Exercise 2 + +Using the skills form part 1, see if you can view the data in blocks 1 and 2. + +Note: the EM4100 ID of 1122334455 is encoded, so don’t expect to see + those bytes as such. To learn how to do that, you guessed it, find the + datasheet and review. + +At this point we have an EM4100 card. If we wanted to password protect +it, we can follow the password section and update the config from +00148040 to 00148050. + +***Important : Don’t forget to set a valid password in block 7 and remember it.*** + +## The configuration Block – Block 3 Page 1 diff --git a/doc/t55xx_block0.png b/doc/t55xx_block0.png new file mode 100644 index 0000000000000000000000000000000000000000..2220adf3f2a24b8d4ff26f254b8942fd0c4e9c96 GIT binary patch literal 26953 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 M#N Date: Tue, 24 Nov 2020 18:45:19 +1100 Subject: [PATCH 10/52] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index e61260b54..d8fba7354 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,7 @@ ||[Advanced compilation parameters](/doc/md/Use_of_Proxmark/4_Advanced-compilation-parameters.md)|[More cheat sheets](https://github.com/RfidResearchGroup/proxmark3/wiki/More-cheat-sheets)| ||**[Troubleshooting](/doc/md/Installation_Instructions/Troubleshooting.md)**|[Complete client command set](/doc/commands.md)| ||**[JTAG](/doc/jtag_notes.md)**|| +|T55xx Guide | **[T55xx Guide](doc/T5577_Guide.md)** || ## Notes / helpful documents From 817f7d0a753544fa6bcfc0d1894c2a5704119264 Mon Sep 17 00:00:00 2001 From: mwalker33 <51802811+mwalker33@users.noreply.github.com> Date: Tue, 24 Nov 2020 18:49:02 +1100 Subject: [PATCH 11/52] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d8fba7354..d9b48922e 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ ||[Advanced compilation parameters](/doc/md/Use_of_Proxmark/4_Advanced-compilation-parameters.md)|[More cheat sheets](https://github.com/RfidResearchGroup/proxmark3/wiki/More-cheat-sheets)| ||**[Troubleshooting](/doc/md/Installation_Instructions/Troubleshooting.md)**|[Complete client command set](/doc/commands.md)| ||**[JTAG](/doc/jtag_notes.md)**|| -|T55xx Guide | **[T55xx Guide](doc/T5577_Guide.md)** || +|||**[T55xx Guide](doc/T5577_Guide.md)**| ## Notes / helpful documents From 2718620e334edace30ca5c63d873092a92bce239 Mon Sep 17 00:00:00 2001 From: mwalker33 <51802811+mwalker33@users.noreply.github.com> Date: Tue, 24 Nov 2020 18:49:54 +1100 Subject: [PATCH 12/52] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d9b48922e..e7941e483 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ ||[Advanced compilation parameters](/doc/md/Use_of_Proxmark/4_Advanced-compilation-parameters.md)|[More cheat sheets](https://github.com/RfidResearchGroup/proxmark3/wiki/More-cheat-sheets)| ||**[Troubleshooting](/doc/md/Installation_Instructions/Troubleshooting.md)**|[Complete client command set](/doc/commands.md)| ||**[JTAG](/doc/jtag_notes.md)**|| -|||**[T55xx Guide](doc/T5577_Guide.md)**| +||**[T55xx Guide](/doc/T5577_Guide.md)**|| ## Notes / helpful documents From cfd848d9ded0a45944fce542ccc1df3f2acfa0d7 Mon Sep 17 00:00:00 2001 From: mwalker33 <51802811+mwalker33@users.noreply.github.com> Date: Tue, 24 Nov 2020 18:52:54 +1100 Subject: [PATCH 13/52] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e7941e483..a3533ea85 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ ||[Advanced compilation parameters](/doc/md/Use_of_Proxmark/4_Advanced-compilation-parameters.md)|[More cheat sheets](https://github.com/RfidResearchGroup/proxmark3/wiki/More-cheat-sheets)| ||**[Troubleshooting](/doc/md/Installation_Instructions/Troubleshooting.md)**|[Complete client command set](/doc/commands.md)| ||**[JTAG](/doc/jtag_notes.md)**|| -||**[T55xx Guide](/doc/T5577_Guide.md)**|| +|||**[T55xx Guide](/doc/T5577_Guide.md)**| ## Notes / helpful documents From 878e9f594f86d0761b49a4ce73e67a353f9f66e5 Mon Sep 17 00:00:00 2001 From: mwalker33 <51802811+mwalker33@users.noreply.github.com> Date: Tue, 24 Nov 2020 18:55:35 +1100 Subject: [PATCH 14/52] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e9bb8d98f..c1bda4ff5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ All notable changes to this project will be documented in this file. This project uses the changelog in accordance with [keepchangelog](http://keepachangelog.com/). Please use this to write notable changes, which is not the same as git commit log... ## [unreleased][unreleased] + - Added T55xx Guide to assist in learning how to use the T55xx chip (@mwalker33) - Fix 'hf iclass wrbl' - dealing with tags in unsecured vs secured pagemode now is correct (@iceman1001) - Change many commands to cliparser (@iceman1001, @tcprst, @mwalker33,...) - ... From 6c2c0108dff406f91f3cc400cf519f1f1b6ea999 Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Tue, 24 Nov 2020 09:16:57 +0100 Subject: [PATCH 15/52] Readme: compact table --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index a3533ea85..bc7e88854 100644 --- a/README.md +++ b/README.md @@ -21,8 +21,7 @@ |[Donations](#Donations)|[Maintainers](/doc/md/Development/Maintainers.md)|[Command Cheat sheet](/doc/cheatsheet.md)| ||[Advanced compilation parameters](/doc/md/Use_of_Proxmark/4_Advanced-compilation-parameters.md)|[More cheat sheets](https://github.com/RfidResearchGroup/proxmark3/wiki/More-cheat-sheets)| ||**[Troubleshooting](/doc/md/Installation_Instructions/Troubleshooting.md)**|[Complete client command set](/doc/commands.md)| -||**[JTAG](/doc/jtag_notes.md)**|| -|||**[T55xx Guide](/doc/T5577_Guide.md)**| +||**[JTAG](/doc/jtag_notes.md)**|[T55xx Guide](/doc/T5577_Guide.md)| ## Notes / helpful documents From f59d57aaabc65b21fb674e71fc1d269b82d96354 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Tue, 24 Nov 2020 12:52:09 +0100 Subject: [PATCH 16/52] lf securakey - now uses cliparser --- client/src/cmdlfsecurakey.c | 97 ++++++++++++++++++++++--------------- 1 file changed, 57 insertions(+), 40 deletions(-) diff --git a/client/src/cmdlfsecurakey.c b/client/src/cmdlfsecurakey.c index b5aab5fa8..7315dc9cd 100644 --- a/client/src/cmdlfsecurakey.c +++ b/client/src/cmdlfsecurakey.c @@ -22,22 +22,10 @@ #include "parity.h" // for wiegand parity test #include "protocols.h" // t55xx defines #include "cmdlft55xx.h" // clone.. +#include "cliparser.h" static int CmdHelp(const char *Cmd); -static int usage_lf_securakey_clone(void) { - PrintAndLogEx(NORMAL, "clone a Securakey tag to a T55x7 tag."); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Usage: lf securakey clone [h] [b ]"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h : this help"); - PrintAndLogEx(NORMAL, " b : raw hex data. 12 bytes max"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" lf securakey clone b 7FCB400001ADEA5344300000")); - return PM3_SUCCESS; -} - //see ASKDemod for what args are accepted int demodSecurakey(bool verbose) { (void) verbose; // unused so far @@ -120,7 +108,7 @@ int demodSecurakey(bool verbose) { PrintAndLogEx(INFO, "\nHow the FC translates to printed FC is unknown"); PrintAndLogEx(INFO, "How the checksum is calculated is unknown"); - PrintAndLogEx(INFO, "Help the community identify this format further\n by sharing your tag on the pm3 forum or with forum members"); + PrintAndLogEx(INFO, "Help the community identify this format further\nby sharing your tag on the pm3 forum or discord"); return PM3_SUCCESS; } @@ -137,36 +125,37 @@ static int CmdSecurakeyRead(const char *Cmd) { static int CmdSecurakeyClone(const char *Cmd) { - uint32_t blocks[4]; - bool errors = false; - uint8_t cmdp = 0; + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf securakey clone", + "clone a Securakey tag to a T55x7 tag.", + "lf securakey clone --raw 7FCB400001ADEA5344300000" + ); + + void *argtable[] = { + arg_param_begin, + arg_str0("r", "raw", "", " raw hex data. 12 bytes max"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + int datalen = 0; + // skip first block, 3*4 = 12 bytes left + uint8_t raw[12] = {0}; + CLIGetHexWithReturn(ctx, 1, raw, &datalen); - while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { - switch (tolower(param_getchar(Cmd, cmdp))) { - case 'h': - return usage_lf_securakey_clone(); - case 'b': { - // skip first block, 3*4 = 12 bytes left - uint8_t rawhex[12] = {0}; - int res = param_gethex_to_eol(Cmd, cmdp + 1, rawhex, sizeof(rawhex), &datalen); - if (res != 0) - errors = true; - - for (uint8_t i = 1; i < ARRAYLEN(blocks); i++) { - blocks[i] = bytes_to_num(rawhex + ((i - 1) * 4), sizeof(uint32_t)); - } - cmdp += 2; - break; - } - default: - PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); - errors = true; - break; + if (datalen > 0) { + if (datalen != 12) { + PrintAndLogEx(ERR, "Data must be 8 bytes (16 HEX characters)"); + CLIParserFree(ctx); + return PM3_EINVARG; } } + CLIParserFree(ctx); - if (errors || cmdp == 0) return usage_lf_securakey_clone(); + uint32_t blocks[4]; + for (uint8_t i = 1; i < ARRAYLEN(blocks); i++) { + blocks[i] = bytes_to_num(raw + ((i - 1) * 4), sizeof(uint32_t)); + } //Securakey - compat mode, ASK/Man, data rate 40, 3 data blocks blocks[0] = T55x7_MODULATION_MANCHESTER | T55x7_BITRATE_RF_40 | 3 << T55x7_MAXBLOCK_SHIFT; @@ -181,7 +170,35 @@ static int CmdSecurakeyClone(const char *Cmd) { } static int CmdSecurakeySim(const char *Cmd) { - PrintAndLogEx(INFO, " To be implemented, feel free to contribute!"); + PrintAndLogEx(INFO, _RED_("To be implemented, feel free to contribute!")); + return PM3_SUCCESS; + + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf securakey sim", + "Enables simulation of viking card with specified card number.\n" + "Simulation runs until the button is pressed or another USB command is issued.", + "lf securakey sim --raw 7FCB400001ADEA5344300000" + ); + + void *argtable[] = { + arg_param_begin, + arg_int0("r", "raw", "", " raw hex data. 12 bytes max"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + int datalen = 0; + // skip first block, 3*4 = 12 bytes left + uint8_t raw[12] = {0}; + CLIGetHexWithReturn(ctx, 1, raw, &datalen); + + if (datalen > 0) { + if (datalen != 12) { + PrintAndLogEx(ERR, "Data must be 8 bytes (16 HEX characters)"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + } + CLIParserFree(ctx); return PM3_SUCCESS; } From d24e0436dc5106250d12366e8459c82a69734f02 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Tue, 24 Nov 2020 12:52:55 +0100 Subject: [PATCH 17/52] hf 15 wrbl - missing some data output --- client/src/cmdhf15.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/cmdhf15.c b/client/src/cmdhf15.c index 593eaaece..3eebbce74 100644 --- a/client/src/cmdhf15.c +++ b/client/src/cmdhf15.c @@ -1725,7 +1725,7 @@ static int CmdHF15Write(const char *Cmd) { AddCrc15(req, reqlen); reqlen += 2; - PrintAndLogEx(INFO, "iso15693 writing to page %02d (0x%02X) | data ", pagenum, pagenum); + PrintAndLogEx(INFO, "iso15693 writing to page %02d (0x%02X) | data [ %s ] ", pagenum, pagenum, sprint_hex(req, reqlen)); PacketResponseNG resp; clearCommandBuffer(); From 3cb12356a28cd5e43708057d2925dfb8c8c5faa7 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Tue, 24 Nov 2020 12:53:29 +0100 Subject: [PATCH 18/52] textual --- client/src/loclass/elite_crack.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/src/loclass/elite_crack.c b/client/src/loclass/elite_crack.c index a559b4e7b..3defe5560 100644 --- a/client/src/loclass/elite_crack.c +++ b/client/src/loclass/elite_crack.c @@ -468,9 +468,9 @@ int calculateMasterKey(uint8_t first16bytes[], uint64_t master_key[]) { mbedtls_des_setkey_enc(&ctx_e, key64_stdformat); mbedtls_des_crypt_ecb(&ctx_e, key64_negated, result); - PrintAndLogEx(NORMAL, "\n"); + PrintAndLogEx(NORMAL, ""); PrintAndLogEx(SUCCESS, "-- High security custom key (Kcus) --"); - PrintAndLogEx(SUCCESS, "Standard format %s", sprint_hex(key64_stdformat, 8)); + PrintAndLogEx(SUCCESS, "Standard format " _GREEN_("%s"), sprint_hex(key64_stdformat, 8)); PrintAndLogEx(SUCCESS, "iClass format %s", sprint_hex(key64, 8)); if (master_key != NULL) From 04e0cdc9cf75656de3d00d556c7fb9323b7ffbae Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Tue, 24 Nov 2020 15:40:13 +0100 Subject: [PATCH 19/52] lf viking - now uses cliparse --- client/src/cmdlfviking.c | 108 +++++++++++++++++++++------------------ 1 file changed, 58 insertions(+), 50 deletions(-) diff --git a/client/src/cmdlfviking.c b/client/src/cmdlfviking.c index 4a5f534ab..a301a2f59 100644 --- a/client/src/cmdlfviking.c +++ b/client/src/cmdlfviking.c @@ -9,13 +9,10 @@ // ASK/Manchester, RF/32, 64 bits (complete) //----------------------------------------------------------------------------- #include "cmdlfviking.h" - #include #include #include - #include "common.h" - #include "cmdparser.h" // command_t #include "comms.h" #include "ui.h" @@ -23,36 +20,10 @@ #include "cmdlf.h" #include "lfdemod.h" #include "commonutil.h" // num_to_bytes +#include "cliparser.h" static int CmdHelp(const char *Cmd); -static int usage_lf_viking_clone(void) { - PrintAndLogEx(NORMAL, "clone a Viking AM tag to a T55x7 or Q5/T5555 tag."); - PrintAndLogEx(NORMAL, "Usage: lf viking clone "); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " : 8 digit hex viking card number"); - PrintAndLogEx(NORMAL, " : specify writing to Q5/T5555 tag)"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" lf viking clone 1A337")); - PrintAndLogEx(NORMAL, _YELLOW_(" lf viking clone 1A337 Q5") " - encode for Q5/T5555 tag"); - return PM3_SUCCESS; -} - -static int usage_lf_viking_sim(void) { - PrintAndLogEx(NORMAL, "Enables simulation of viking card with specified card number."); - PrintAndLogEx(NORMAL, "Simulation runs until the button is pressed or another USB command is issued."); - PrintAndLogEx(NORMAL, "Per viking format, the card number is 8 digit hex number. Larger values are truncated."); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Usage: lf viking sim "); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " : 8 digit hex viking card number"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" lf viking sim 1A337")); - return PM3_SUCCESS; -} - //see ASKDemod for what args are accepted int demodViking(bool verbose) { (void) verbose; // unused so far @@ -95,31 +66,48 @@ static int CmdVikingRead(const char *Cmd) { } static int CmdVikingClone(const char *Cmd) { - uint32_t id = 0; - uint64_t rawID = 0; - bool Q5 = false; - char cmdp = tolower(param_getchar(Cmd, 0)); - if (strlen(Cmd) == 0 || cmdp == 'h') return usage_lf_viking_clone(); - id = param_get32ex(Cmd, 0, 0, 16); - if (id == 0) return usage_lf_viking_clone(); + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf viking clone", + "clone a Viking AM tag to a T55x7 or Q5/T5555 tag.", + "lf viking clone --cn 01A337\n" + "lf viking clone --cn 01A337 --q5 -> encode for Q5/T5555 tag" + ); - cmdp = tolower(param_getchar(Cmd, 1)); - if (cmdp == 'q') - Q5 = true; + void *argtable[] = { + arg_param_begin, + arg_strx0(NULL, "cn", "", "8 digit hex viking card number"), + arg_lit0(NULL, "q5", "optional - specify writing to Q5/T5555 tag"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); - rawID = getVikingBits(id); + int raw_len = 0; + uint8_t raw[4] = {0}; + CLIGetHexWithReturn(ctx, 1, raw, &raw_len); + + uint32_t id = bytes_to_num(raw, raw_len); + if (id == 0) { + PrintAndLogEx(ERR, "Cardnumber can't be zero"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + + bool q5 = arg_get_lit(ctx, 2); + CLIParserFree(ctx); + + uint64_t rawID = getVikingBits(id); struct p { bool Q5; uint8_t blocks[8]; } PACKED payload; - payload.Q5 = Q5; + payload.Q5 = q5; num_to_bytes(rawID, 8, &payload.blocks[0]); PrintAndLogEx(INFO, "Preparing to clone Viking tag on " _YELLOW_("%s") " - ID " _YELLOW_("%08X")" raw " _YELLOW_("%s") - , (Q5) ? "Q5/T5555" : "T55x7" + , (q5) ? "Q5/T5555" : "T55x7" , id , sprint_hex(payload.blocks, sizeof(payload.blocks)) ); @@ -138,15 +126,35 @@ static int CmdVikingClone(const char *Cmd) { } static int CmdVikingSim(const char *Cmd) { - uint32_t id = 0; - uint64_t rawID = 0; - char cmdp = tolower(param_getchar(Cmd, 0)); - if (strlen(Cmd) == 0 || cmdp == 'h') return usage_lf_viking_sim(); - id = param_get32ex(Cmd, 0, 0, 16); - if (id == 0) return usage_lf_viking_sim(); + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf viking sim", + "Enables simulation of viking card with specified card number.\n" + "Simulation runs until the button is pressed or another USB command is issued.\n" + "Per viking format, the card number is 8 digit hex number. Larger values are truncated.", + "lf viking sim --cn 01A337" + ); - rawID = getVikingBits(id); + void *argtable[] = { + arg_param_begin, + arg_strx0(NULL, "cn", "", "8 digit hex viking card number"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + int raw_len = 0; + uint8_t raw[4] = {0}; + CLIGetHexWithReturn(ctx, 1, raw, &raw_len); + + uint32_t id = bytes_to_num(raw, raw_len); + if (id == 0) { + PrintAndLogEx(ERR, "Cardnumber can't be zero"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + CLIParserFree(ctx); + + uint64_t rawID = getVikingBits(id); PrintAndLogEx(SUCCESS, "Simulating Viking - ID " _YELLOW_("%08X") " raw " _YELLOW_("%08X%08X"), id, (uint32_t)(rawID >> 32), (uint32_t)(rawID & 0xFFFFFFFF)); From c25ad02f88a33d1bbb95dfa3857ac0c420eff3ee Mon Sep 17 00:00:00 2001 From: tcprst Date: Tue, 24 Nov 2020 15:50:10 -0500 Subject: [PATCH 20/52] add iclass keys found on pastebin to dic --- client/dictionaries/iclass_default_keys.dic | 2 ++ 1 file changed, 2 insertions(+) diff --git a/client/dictionaries/iclass_default_keys.dic b/client/dictionaries/iclass_default_keys.dic index 22e1ee653..9f03720c9 100644 --- a/client/dictionaries/iclass_default_keys.dic +++ b/client/dictionaries/iclass_default_keys.dic @@ -10,3 +10,5 @@ AEA684A6DAB23278 # AA1 F0E1D2C3B4A59687 # Kd from PicoPass 2k documentation 5CBCF1DA45D5FB4F # PicoPass Default Exchange Key 31ad7ebd2f282168 # From HID multiclassSE reader +6EFD46EFCBB3C875 # From pastebin: https://pastebin.com/uHqpjiuU +E033CA419AEE43F9 # From pastebin: https://pastebin.com/uHqpjiuU \ No newline at end of file From 1e6fcd0291e55e4c37be58c3970a170c7f279912 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Wed, 25 Nov 2020 10:51:54 +0100 Subject: [PATCH 21/52] added lf securakey sim - untested --- client/src/cmdlfsecurakey.c | 63 +++++++++++++++++++++++-------------- client/src/util.c | 19 +++++++++++ client/src/util.h | 1 + 3 files changed, 60 insertions(+), 23 deletions(-) diff --git a/client/src/cmdlfsecurakey.c b/client/src/cmdlfsecurakey.c index 7315dc9cd..4582f1205 100644 --- a/client/src/cmdlfsecurakey.c +++ b/client/src/cmdlfsecurakey.c @@ -8,10 +8,8 @@ // ASK/Manchester, RF/40, 96 bits long (unknown cs) //----------------------------------------------------------------------------- #include "cmdlfsecurakey.h" - #include // memcpy #include // tolower - #include "commonutil.h" // ARRAYLEN #include "cmdparser.h" // command_t #include "comms.h" @@ -133,22 +131,20 @@ static int CmdSecurakeyClone(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_str0("r", "raw", "", " raw hex data. 12 bytes max"), + arg_str0("r", "raw", "", " raw hex data. 12 bytes"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); - int datalen = 0; + int raw_len = 0; // skip first block, 3*4 = 12 bytes left uint8_t raw[12] = {0}; - CLIGetHexWithReturn(ctx, 1, raw, &datalen); + CLIGetHexWithReturn(ctx, 1, raw, &raw_len); - if (datalen > 0) { - if (datalen != 12) { - PrintAndLogEx(ERR, "Data must be 8 bytes (16 HEX characters)"); - CLIParserFree(ctx); - return PM3_EINVARG; - } + if (raw_len != 12) { + PrintAndLogEx(ERR, "Data must be 12 bytes (24 HEX characters)"); + CLIParserFree(ctx); + return PM3_EINVARG; } CLIParserFree(ctx); @@ -170,35 +166,56 @@ static int CmdSecurakeyClone(const char *Cmd) { } static int CmdSecurakeySim(const char *Cmd) { - PrintAndLogEx(INFO, _RED_("To be implemented, feel free to contribute!")); - return PM3_SUCCESS; CLIParserContext *ctx; CLIParserInit(&ctx, "lf securakey sim", - "Enables simulation of viking card with specified card number.\n" + "Enables simulation of secura card with specified card number.\n" "Simulation runs until the button is pressed or another USB command is issued.", "lf securakey sim --raw 7FCB400001ADEA5344300000" ); void *argtable[] = { arg_param_begin, - arg_int0("r", "raw", "", " raw hex data. 12 bytes max"), + arg_str0("r", "raw", "", " raw hex data. 12 bytes"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); - int datalen = 0; + + int raw_len = 0; // skip first block, 3*4 = 12 bytes left uint8_t raw[12] = {0}; - CLIGetHexWithReturn(ctx, 1, raw, &datalen); + CLIGetHexWithReturn(ctx, 1, raw, &raw_len); - if (datalen > 0) { - if (datalen != 12) { - PrintAndLogEx(ERR, "Data must be 8 bytes (16 HEX characters)"); - CLIParserFree(ctx); - return PM3_EINVARG; - } + if (raw_len != 12) { + PrintAndLogEx(ERR, "Data must be 12 bytes (24 HEX characters) %d", raw_len); + CLIParserFree(ctx); + return PM3_EINVARG; } CLIParserFree(ctx); + + PrintAndLogEx(SUCCESS, "Simulating SecuraKey - raw " _YELLOW_("%s"), sprint_hex_inrow(raw, sizeof(raw))); + + uint8_t bs[sizeof(raw) * 8]; + bytes_to_bytebits(raw, sizeof(raw), bs); + + lf_asksim_t *payload = calloc(1, sizeof(lf_asksim_t) + sizeof(bs)); + payload->encoding = 1; + payload->invert = 0; + payload->separator = 0; + payload->clock = 40; + memcpy(payload->data, bs, sizeof(bs)); + + clearCommandBuffer(); + SendCommandNG(CMD_LF_ASK_SIMULATE, (uint8_t *)payload, sizeof(lf_asksim_t) + sizeof(bs)); + free(payload); + + PacketResponseNG resp; + WaitForResponse(CMD_LF_ASK_SIMULATE, &resp); + + PrintAndLogEx(INFO, "Done"); + if (resp.status != PM3_EOPABORTED) + return resp.status; + return PM3_SUCCESS; } diff --git a/client/src/util.c b/client/src/util.c index 76a2d7885..e9fd171e2 100644 --- a/client/src/util.c +++ b/client/src/util.c @@ -446,6 +446,25 @@ void num_to_bytebitsLSBF(uint64_t n, size_t len, uint8_t *dest) { } } +void bytes_to_bytebits(void* src, size_t srclen, void* dest) { + + uint8_t *s = (uint8_t*)src; + uint8_t *d = (uint8_t*)dest; + + uint32_t i = srclen * 8; + while (srclen--) { + uint8_t b = s[srclen]; + d[--i] = (b >> 0) & 1; + d[--i] = (b >> 1) & 1; + d[--i] = (b >> 2) & 1; + d[--i] = (b >> 3) & 1; + d[--i] = (b >> 4) & 1; + d[--i] = (b >> 5) & 1; + d[--i] = (b >> 6) & 1; + d[--i] = (b >> 7) & 1; + } +} + // aa,bb,cc,dd,ee,ff,gg,hh, ii,jj,kk,ll,mm,nn,oo,pp // to // hh,gg,ff,ee,dd,cc,bb,aa, pp,oo,nn,mm,ll,kk,jj,ii diff --git a/client/src/util.h b/client/src/util.h index 51100731e..28ca74917 100644 --- a/client/src/util.h +++ b/client/src/util.h @@ -56,6 +56,7 @@ void print_blocks(uint32_t *data, size_t len); int hex_to_bytes(const char *hexValue, uint8_t *bytesValue, size_t maxBytesValueLen); void num_to_bytebits(uint64_t n, size_t len, uint8_t *dest); void num_to_bytebitsLSBF(uint64_t n, size_t len, uint8_t *dest); +void bytes_to_bytebits(void* src, size_t srclen, void* dest); // Swap endian on arrays up to 64bytes. uint8_t *SwapEndian64(const uint8_t *src, const size_t len, const uint8_t blockSize); From 2ff7221874f68cf2353077f6dc4a30051dbb4819 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Wed, 25 Nov 2020 13:34:52 +0100 Subject: [PATCH 22/52] lf viking read -> now renamed and supports contiuous mode --- client/src/cmdlfviking.c | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/client/src/cmdlfviking.c b/client/src/cmdlfviking.c index a301a2f59..b388ceff7 100644 --- a/client/src/cmdlfviking.c +++ b/client/src/cmdlfviking.c @@ -59,10 +59,30 @@ static int CmdVikingDemod(const char *Cmd) { } //see ASKDemod for what args are accepted -static int CmdVikingRead(const char *Cmd) { - (void)Cmd; - lf_read(false, 10000); - return demodViking(true); +static int CmdVikingReader(const char *Cmd) { + + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf viking reader", + "read a Viking AM tag", + "lf viking reader -@ -> continuous reader mode" + ); + + void *argtable[] = { + arg_param_begin, + arg_lit0("@", NULL, "optional - continuous reader mode"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + bool cm = arg_get_lit(ctx, 1); + CLIParserFree(ctx); + + do { + + lf_read(false, 10000); + demodViking(true); + } while (cm && !kbd_enter_pressed()); + + return PM3_SUCCESS; } static int CmdVikingClone(const char *Cmd) { @@ -184,7 +204,7 @@ static int CmdVikingSim(const char *Cmd) { static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help"}, {"demod", CmdVikingDemod, AlwaysAvailable, "Demodulate a Viking tag from the GraphBuffer"}, - {"read", CmdVikingRead, IfPm3Lf, "Attempt to read and Extract tag data from the antenna"}, + {"reader", CmdVikingReader, IfPm3Lf, "Attempt to read and Extract tag data from the antenna"}, {"clone", CmdVikingClone, IfPm3Lf, "clone Viking tag to T55x7 or Q5/T5555"}, {"sim", CmdVikingSim, IfPm3Lf, "simulate Viking tag"}, {NULL, NULL, NULL, NULL} From 2ad7a5b7f1e601c6dd575369dd65eec67a44af0a Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Wed, 25 Nov 2020 13:39:32 +0100 Subject: [PATCH 23/52] lf securakey read - renamed and now support contiuous mode --- client/src/cmdlfsecurakey.c | 44 +++++++++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 12 deletions(-) diff --git a/client/src/cmdlfsecurakey.c b/client/src/cmdlfsecurakey.c index 4582f1205..4e2dc80f3 100644 --- a/client/src/cmdlfsecurakey.c +++ b/client/src/cmdlfsecurakey.c @@ -104,9 +104,11 @@ int demodSecurakey(bool verbose) { if (bitLen <= 32) PrintAndLogEx(SUCCESS, "Wiegand: " _GREEN_("%08X") " parity (%s)", (lWiegand << (bitLen / 2)) | rWiegand, parity ? _GREEN_("ok") : _RED_("fail")); - PrintAndLogEx(INFO, "\nHow the FC translates to printed FC is unknown"); - PrintAndLogEx(INFO, "How the checksum is calculated is unknown"); - PrintAndLogEx(INFO, "Help the community identify this format further\nby sharing your tag on the pm3 forum or discord"); + if ( verbose ) { + PrintAndLogEx(INFO, "\nHow the FC translates to printed FC is unknown"); + PrintAndLogEx(INFO, "How the checksum is calculated is unknown"); + PrintAndLogEx(INFO, "Help the community identify this format further\nby sharing your tag on the pm3 forum or discord"); + } return PM3_SUCCESS; } @@ -115,10 +117,28 @@ static int CmdSecurakeyDemod(const char *Cmd) { return demodSecurakey(true); } -static int CmdSecurakeyRead(const char *Cmd) { - (void)Cmd; // Cmd is not used so far - lf_read(false, 8000); - return demodSecurakey(true); +static int CmdSecurakeyReader(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf securakey reader", + "read a Securakey tag", + "lf securakey reader -@ -> continuous reader mode" + ); + + void *argtable[] = { + arg_param_begin, + arg_lit0("@", NULL, "optional - continuous reader mode"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + bool cm = arg_get_lit(ctx, 1); + CLIParserFree(ctx); + + do { + lf_read(false, 8000); + demodSecurakey(!cm); + } while (cm && !kbd_enter_pressed()); + + return PM3_SUCCESS; } static int CmdSecurakeyClone(const char *Cmd) { @@ -220,11 +240,11 @@ static int CmdSecurakeySim(const char *Cmd) { } static command_t CommandTable[] = { - {"help", CmdHelp, AlwaysAvailable, "This help"}, - {"demod", CmdSecurakeyDemod, AlwaysAvailable, "Demodulate an Securakey tag from the GraphBuffer"}, - {"read", CmdSecurakeyRead, IfPm3Lf, "Attempt to read and extract tag data from the antenna"}, - {"clone", CmdSecurakeyClone, IfPm3Lf, "clone Securakey tag to T55x7"}, - {"sim", CmdSecurakeySim, IfPm3Lf, "simulate Securakey tag"}, + {"help", CmdHelp, AlwaysAvailable, "This help"}, + {"demod", CmdSecurakeyDemod, AlwaysAvailable, "Demodulate an Securakey tag from the GraphBuffer"}, + {"reader", CmdSecurakeyReader, IfPm3Lf, "Attempt to read and extract tag data from the antenna"}, + {"clone", CmdSecurakeyClone, IfPm3Lf, "clone Securakey tag to T55x7"}, + {"sim", CmdSecurakeySim, IfPm3Lf, "simulate Securakey tag"}, {NULL, NULL, NULL, NULL} }; From 0cb3704eee0e9b710331d44acdd9b237bfa6a725 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Wed, 25 Nov 2020 14:43:09 +0100 Subject: [PATCH 24/52] textual --- client/src/cmdlfsecurakey.c | 2 +- client/src/cmdlfviking.c | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/client/src/cmdlfsecurakey.c b/client/src/cmdlfsecurakey.c index 4e2dc80f3..c5bd2ff0d 100644 --- a/client/src/cmdlfsecurakey.c +++ b/client/src/cmdlfsecurakey.c @@ -181,7 +181,7 @@ static int CmdSecurakeyClone(const char *Cmd) { int res = clone_t55xx_tag(blocks, ARRAYLEN(blocks)); PrintAndLogEx(SUCCESS, "Done"); - PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf securakey read`") " to verify"); + PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf securakey reader`") " to verify"); return res; } diff --git a/client/src/cmdlfviking.c b/client/src/cmdlfviking.c index b388ceff7..d4ab057c8 100644 --- a/client/src/cmdlfviking.c +++ b/client/src/cmdlfviking.c @@ -77,7 +77,6 @@ static int CmdVikingReader(const char *Cmd) { CLIParserFree(ctx); do { - lf_read(false, 10000); demodViking(true); } while (cm && !kbd_enter_pressed()); @@ -141,7 +140,7 @@ static int CmdVikingClone(const char *Cmd) { return PM3_ETIMEOUT; } PrintAndLogEx(SUCCESS, "Done"); - PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf viking read`") " to verify"); + PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf viking reader`") " to verify"); return resp.status; } @@ -204,7 +203,7 @@ static int CmdVikingSim(const char *Cmd) { static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help"}, {"demod", CmdVikingDemod, AlwaysAvailable, "Demodulate a Viking tag from the GraphBuffer"}, - {"reader", CmdVikingReader, IfPm3Lf, "Attempt to read and Extract tag data from the antenna"}, + {"reader", CmdVikingReader, IfPm3Lf, "Attempt to read and Extract tag data from the antenna"}, {"clone", CmdVikingClone, IfPm3Lf, "clone Viking tag to T55x7 or Q5/T5555"}, {"sim", CmdVikingSim, IfPm3Lf, "simulate Viking tag"}, {NULL, NULL, NULL, NULL} From 63e92c187c53416c04cd99858dabcd601f2a03d6 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Wed, 25 Nov 2020 14:43:39 +0100 Subject: [PATCH 25/52] lf visa - now uses cliparser and reader continuous mode --- client/src/cmdlfvisa2000.c | 133 ++++++++++++++++++++----------------- 1 file changed, 72 insertions(+), 61 deletions(-) diff --git a/client/src/cmdlfvisa2000.c b/client/src/cmdlfvisa2000.c index 7fc3f89a5..04e16e727 100644 --- a/client/src/cmdlfvisa2000.c +++ b/client/src/cmdlfvisa2000.c @@ -27,41 +27,14 @@ #include "lfdemod.h" // parityTest #include "cmdlft55xx.h" // write verify #include "cmdlfem4x05.h" // +#include "cliparser.h" -#define BL0CK1 0x56495332 +#ifndef VISA2k_BL0CK1 +#define VISA2k_BL0CK1 0x56495332 +#endif static int CmdHelp(const char *Cmd); -static int usage_lf_visa2k_clone(void) { - PrintAndLogEx(NORMAL, "clone a Visa2000 tag to a T55x7 or Q5/T5555 tag."); - PrintAndLogEx(NORMAL, "Usage: lf visa2000 clone [h] "); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h : This help"); - PrintAndLogEx(NORMAL, " : Visa2k card ID"); - PrintAndLogEx(NORMAL, " : specify writing to Q5/T5555 tag"); - PrintAndLogEx(NORMAL, " : specify writing to EM4305/4469 tag"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" lf visa2000 clone 112233")); - PrintAndLogEx(NORMAL, _YELLOW_(" lf visa2000 clone 112233 q5") " -- encode for Q5/T5555"); - PrintAndLogEx(NORMAL, _YELLOW_(" lf visa2000 clone 112233 em4305") " -- encode for EM4305/4469"); - return PM3_SUCCESS; -} - -static int usage_lf_visa2k_sim(void) { - PrintAndLogEx(NORMAL, "Enables simulation of visa2k card with specified card number."); - PrintAndLogEx(NORMAL, "Simulation runs until the button is pressed or another USB command is issued."); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Usage: lf visa2000 sim [h] "); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h : This help"); - PrintAndLogEx(NORMAL, " : Visa2k card ID"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" lf visa2000 sim 112233")); - return PM3_SUCCESS; -} - static uint8_t visa_chksum(uint32_t id) { uint8_t sum = 0; for (uint8_t i = 0; i < 32; i += 4) @@ -165,43 +138,72 @@ static int CmdVisa2kDemod(const char *Cmd) { } // 64*96*2=12288 samples just in case we just missed the first preamble we can still catch 2 of them -static int CmdVisa2kRead(const char *Cmd) { - (void)Cmd; // Cmd is not used so far - lf_read(false, 20000); - return demodVisa2k(true); +static int CmdVisa2kReader(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf visa2000 reader", + "read a visa2000 tag", + "lf visa2000 reader -@ -> continuous reader mode" + ); + + void *argtable[] = { + arg_param_begin, + arg_lit0("@", NULL, "optional - continuous reader mode"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + bool cm = arg_get_lit(ctx, 1); + CLIParserFree(ctx); + + do { + lf_read(false, 20000); + demodVisa2k(!cm); + } while (cm && !kbd_enter_pressed()); + return PM3_SUCCESS; } static int CmdVisa2kClone(const char *Cmd) { - uint64_t id = 0; - uint32_t blocks[4] = {T55x7_MODULATION_MANCHESTER | T55x7_BITRATE_RF_64 | T55x7_ST_TERMINATOR | 3 << T55x7_MAXBLOCK_SHIFT, BL0CK1, 0}; + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf visa2000 clone", + "clone a Visa2000 tag to a T55x7, Q5/T5555 or EM4305/4469 tag.", + "lf visa2000 clone --cn 112233\n" + "lf visa2000 clone --cn 112233 --q5 -> encode for Q5/T5555 tag\n" + "lf visa2000 clone --cn 112233 --em -> encode for EM4305/4469" + ); - char cmdp = tolower(param_getchar(Cmd, 0)); - if (strlen(Cmd) == 0 || cmdp == 'h') - return usage_lf_visa2k_clone(); + void *argtable[] = { + arg_param_begin, + arg_u64_1(NULL, "cn", "", "Visa2k card ID"), + arg_lit0(NULL, "q5", "optional - specify writing to Q5/T5555 tag"), + arg_lit0(NULL, "em", "optional - specify writing to EM4305/4469 tag"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); - id = param_get32ex(Cmd, 0, 0, 10); + uint32_t id = arg_get_u32_def(ctx, 1, 0); + bool q5 = arg_get_lit(ctx, 2); + bool em = arg_get_lit(ctx, 3); + CLIParserFree(ctx); + if (q5 && em) { + PrintAndLogEx(FAILED, "Can't specify both Q5 and EM4305 at the same time"); + return PM3_EINVARG; + } + + uint32_t blocks[4] = {T55x7_MODULATION_MANCHESTER | T55x7_BITRATE_RF_64 | T55x7_ST_TERMINATOR | 3 << T55x7_MAXBLOCK_SHIFT, VISA2k_BL0CK1, 0}; char cardtype[16] = {"T55x7"}; // Q5 - bool q5 = tolower(param_getchar(Cmd, 1)) == 'q'; if (q5) { blocks[0] = T5555_FIXED | T5555_MODULATION_MANCHESTER | T5555_SET_BITRATE(64) | T5555_ST_TERMINATOR | 3 << T5555_MAXBLOCK_SHIFT; snprintf(cardtype, sizeof(cardtype), "Q5/T5555"); } // EM4305 - bool em = tolower(param_getchar(Cmd, 1)) == 'e'; if (em) { blocks[0] = EM4305_VISA2000_CONFIG_BLOCK; snprintf(cardtype, sizeof(cardtype), "EM4305/4469"); } - if (q5 && em) { - PrintAndLogEx(FAILED, "Can't specify both Q5 and EM4305 at the same time"); - return PM3_EINVARG; - } - blocks[2] = id; blocks[3] = (visa_parity(id) << 4) | visa_chksum(id); @@ -215,22 +217,31 @@ static int CmdVisa2kClone(const char *Cmd) { res = clone_t55xx_tag(blocks, ARRAYLEN(blocks)); } PrintAndLogEx(SUCCESS, "Done"); - PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf visa2000 read`") " to verify"); + PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf visa2000 reader`") " to verify"); return res; } static int CmdVisa2kSim(const char *Cmd) { - uint32_t id = 0; - char cmdp = tolower(param_getchar(Cmd, 0)); - if (strlen(Cmd) == 0 || cmdp == 'h') - return usage_lf_visa2k_sim(); + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf visa2000 sim", + "Enables simulation of visa2k card with specified card number.\n" + "Simulation runs until the button is pressed or another USB command is issued.\n", + "lf visa2000 sim --cn 1337" + ); - id = param_get32ex(Cmd, 0, 0, 10); + void *argtable[] = { + arg_param_begin, + arg_u64_1(NULL, "cn", "", "Visa2k card ID"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + uint32_t id = arg_get_u32_def(ctx, 1, 0); + CLIParserFree(ctx); - PrintAndLogEx(SUCCESS, "Simulating Visa2000 - CardId: %u", id); + PrintAndLogEx(SUCCESS, "Simulating Visa2000 - CardId:" _YELLOW_("%u"), id); - uint32_t blocks[3] = { BL0CK1, id, (visa_parity(id) << 4) | visa_chksum(id) }; + uint32_t blocks[3] = { VISA2k_BL0CK1, id, (visa_parity(id) << 4) | visa_chksum(id) }; uint8_t bs[96]; for (int i = 0; i < 3; ++i) @@ -257,11 +268,11 @@ static int CmdVisa2kSim(const char *Cmd) { } static command_t CommandTable[] = { - {"help", CmdHelp, AlwaysAvailable, "This help"}, - {"demod", CmdVisa2kDemod, AlwaysAvailable, "demodulate an VISA2000 tag from the GraphBuffer"}, - {"read", CmdVisa2kRead, IfPm3Lf, "attempt to read and extract tag data from the antenna"}, - {"clone", CmdVisa2kClone, IfPm3Lf, "clone Visa2000 tag to T55x7 or Q5/T5555"}, - {"sim", CmdVisa2kSim, IfPm3Lf, "simulate Visa2000 tag"}, + {"help", CmdHelp, AlwaysAvailable, "This help"}, + {"demod", CmdVisa2kDemod, AlwaysAvailable, "demodulate an VISA2000 tag from the GraphBuffer"}, + {"reader", CmdVisa2kReader, IfPm3Lf, "attempt to read and extract tag data from the antenna"}, + {"clone", CmdVisa2kClone, IfPm3Lf, "clone Visa2000 tag to T55x7 or Q5/T5555"}, + {"sim", CmdVisa2kSim, IfPm3Lf, "simulate Visa2000 tag"}, {NULL, NULL, NULL, NULL} }; From b7c0d6aa5e5cc0287096333790f7525526696182 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Wed, 25 Nov 2020 15:13:32 +0100 Subject: [PATCH 26/52] lf viking clone - now supports clone to EM4305/4469 (untested) --- armsrc/appmain.c | 3 ++- armsrc/lfops.c | 7 +++++-- armsrc/lfops.h | 2 +- client/src/cmdlfviking.c | 26 ++++++++++++++++++++------ 4 files changed, 28 insertions(+), 10 deletions(-) diff --git a/armsrc/appmain.c b/armsrc/appmain.c index 69b628d7a..f43de6f47 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -1037,10 +1037,11 @@ static void PacketReceived(PacketCommandNG *packet) { case CMD_LF_VIKING_CLONE: { struct p { bool Q5; + bool EM; uint8_t blocks[8]; } PACKED; struct p *payload = (struct p *)packet->data.asBytes; - CopyVikingtoT55xx(payload->blocks, payload->Q5); + CopyVikingtoT55xx(payload->blocks, payload->Q5, payload->EM); break; } case CMD_LF_COTAG_READ: { diff --git a/armsrc/lfops.c b/armsrc/lfops.c index a6fbb7bba..ac34c2a69 100644 --- a/armsrc/lfops.c +++ b/armsrc/lfops.c @@ -2252,11 +2252,14 @@ void CopyHIDtoT55x7(uint32_t hi2, uint32_t hi, uint32_t lo, uint8_t longFMT) { } // clone viking tag to T55xx -void CopyVikingtoT55xx(uint8_t *blocks, uint8_t Q5) { +void CopyVikingtoT55xx(uint8_t *blocks, bool q5, bool em) { uint32_t data[] = {T55x7_BITRATE_RF_32 | T55x7_MODULATION_MANCHESTER | (2 << T55x7_MAXBLOCK_SHIFT), 0, 0}; - if (Q5) + if (q5) { data[0] = T5555_SET_BITRATE(32) | T5555_MODULATION_MANCHESTER | 2 << T5555_MAXBLOCK_SHIFT; + } else if (em) { + data[0] = (EM4x05_SET_BITRATE(32) | EM4x05_MODULATION_MANCHESTER | EM4x05_SET_NUM_BLOCKS(2) ); + } data[1] = bytes_to_num(blocks, 4); data[2] = bytes_to_num(blocks + 4, 4); diff --git a/armsrc/lfops.h b/armsrc/lfops.h index 0ec050158..703d48b21 100644 --- a/armsrc/lfops.h +++ b/armsrc/lfops.h @@ -41,7 +41,7 @@ int lf_io_watch(int findone, uint32_t *high, uint32_t *low); void CopyHIDtoT55x7(uint32_t hi2, uint32_t hi, uint32_t lo, uint8_t longFMT); // Clone an HID card to T5557/T5567 -void CopyVikingtoT55xx(uint8_t *blocks, uint8_t Q5); +void CopyVikingtoT55xx(uint8_t *blocks, bool q5, bool em); int copy_em410x_to_t55xx(uint8_t card, uint8_t clock, uint32_t id_hi, uint32_t id_lo); diff --git a/client/src/cmdlfviking.c b/client/src/cmdlfviking.c index d4ab057c8..2d60c4893 100644 --- a/client/src/cmdlfviking.c +++ b/client/src/cmdlfviking.c @@ -88,15 +88,17 @@ static int CmdVikingClone(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "lf viking clone", - "clone a Viking AM tag to a T55x7 or Q5/T5555 tag.", + "clone a Viking AM tag to a T55x7, Q5/T5555 or EM4305/4469 tag.", "lf viking clone --cn 01A337\n" "lf viking clone --cn 01A337 --q5 -> encode for Q5/T5555 tag" + "lf viking clone --cn 112233 --em -> encode for EM4305/4469" ); void *argtable[] = { arg_param_begin, arg_strx0(NULL, "cn", "", "8 digit hex viking card number"), arg_lit0(NULL, "q5", "optional - specify writing to Q5/T5555 tag"), + arg_lit0(NULL, "em", "optional - specify writing to EM4305/4469 tag"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -104,29 +106,41 @@ static int CmdVikingClone(const char *Cmd) { int raw_len = 0; uint8_t raw[4] = {0}; CLIGetHexWithReturn(ctx, 1, raw, &raw_len); + bool q5 = arg_get_lit(ctx, 2); + bool em = arg_get_lit(ctx, 3); + CLIParserFree(ctx); uint32_t id = bytes_to_num(raw, raw_len); if (id == 0) { PrintAndLogEx(ERR, "Cardnumber can't be zero"); - CLIParserFree(ctx); return PM3_EINVARG; } - bool q5 = arg_get_lit(ctx, 2); - CLIParserFree(ctx); + if (q5 && em) { + PrintAndLogEx(FAILED, "Can't specify both Q5 and EM4305 at the same time"); + return PM3_EINVARG; + } uint64_t rawID = getVikingBits(id); struct p { bool Q5; + bool EM; uint8_t blocks[8]; } PACKED payload; payload.Q5 = q5; + payload.EM = em; num_to_bytes(rawID, 8, &payload.blocks[0]); + char cardtype[16] = {"T55x7"}; + if (q5) + snprintf(cardtype, sizeof(cardtype), "Q5/T5555"); + else if (em) + snprintf(cardtype, sizeof(cardtype), "EM4305/4469"); + PrintAndLogEx(INFO, "Preparing to clone Viking tag on " _YELLOW_("%s") " - ID " _YELLOW_("%08X")" raw " _YELLOW_("%s") - , (q5) ? "Q5/T5555" : "T55x7" + , cardtype , id , sprint_hex(payload.blocks, sizeof(payload.blocks)) ); @@ -228,7 +242,7 @@ uint64_t getVikingBits(uint32_t id) { ret |= checksum; return ret; } -// by marshmellow + // find viking preamble 0xF200 in already demoded data int detectViking(uint8_t *src, size_t *size) { //make sure buffer has data From a12f32efa648b5b3dd5a80c397767159fabf4dc9 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Wed, 25 Nov 2020 16:20:44 +0100 Subject: [PATCH 27/52] lf securakey clone - now supports EM4305/4469 (untested) --- client/src/cmdlfsecurakey.c | 42 ++++++++++++++++++++++++++++++------- 1 file changed, 35 insertions(+), 7 deletions(-) diff --git a/client/src/cmdlfsecurakey.c b/client/src/cmdlfsecurakey.c index c5bd2ff0d..e54d0ff9c 100644 --- a/client/src/cmdlfsecurakey.c +++ b/client/src/cmdlfsecurakey.c @@ -21,6 +21,7 @@ #include "protocols.h" // t55xx defines #include "cmdlft55xx.h" // clone.. #include "cliparser.h" +#include "cmdlfem4x05.h" // EM defines static int CmdHelp(const char *Cmd); @@ -145,13 +146,17 @@ static int CmdSecurakeyClone(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "lf securakey clone", - "clone a Securakey tag to a T55x7 tag.", - "lf securakey clone --raw 7FCB400001ADEA5344300000" + "clone a Securakey tag to a T55x7, Q5/T5555 or EM4305/4469 tag.", + "lf securakey clone --raw 7FCB400001ADEA5344300000\n" + "lf securakey clone --q5 --raw 7FCB400001ADEA5344300000 -> encode for Q5/T5555 tag\n" + "lf securakey clone --em --raw 7FCB400001ADEA5344300000 -> encode for EM4305/4469" ); void *argtable[] = { arg_param_begin, - arg_str0("r", "raw", "", " raw hex data. 12 bytes"), + arg_str0("r", "raw", "", "raw hex data. 12 bytes"), + arg_lit0(NULL, "q5", "optional - specify writing to Q5/T5555 tag"), + arg_lit0(NULL, "em", "optional - specify writing to EM4305/4469 tag"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -160,13 +165,19 @@ static int CmdSecurakeyClone(const char *Cmd) { // skip first block, 3*4 = 12 bytes left uint8_t raw[12] = {0}; CLIGetHexWithReturn(ctx, 1, raw, &raw_len); + bool q5 = arg_get_lit(ctx, 2); + bool em = arg_get_lit(ctx, 3); + CLIParserFree(ctx); + + if (q5 && em) { + PrintAndLogEx(FAILED, "Can't specify both Q5 and EM4305 at the same time"); + return PM3_EINVARG; + } if (raw_len != 12) { PrintAndLogEx(ERR, "Data must be 12 bytes (24 HEX characters)"); - CLIParserFree(ctx); return PM3_EINVARG; } - CLIParserFree(ctx); uint32_t blocks[4]; for (uint8_t i = 1; i < ARRAYLEN(blocks); i++) { @@ -175,11 +186,28 @@ static int CmdSecurakeyClone(const char *Cmd) { //Securakey - compat mode, ASK/Man, data rate 40, 3 data blocks blocks[0] = T55x7_MODULATION_MANCHESTER | T55x7_BITRATE_RF_40 | 3 << T55x7_MAXBLOCK_SHIFT; + char cardtype[16] = {"T55x7"}; + // Q5 + if (q5) { + blocks[0] = T5555_FIXED | T5555_MODULATION_MANCHESTER | T5555_SET_BITRATE(40) | T5555_ST_TERMINATOR | 3 << T5555_MAXBLOCK_SHIFT; + snprintf(cardtype, sizeof(cardtype), "Q5/T5555"); + } - PrintAndLogEx(INFO, "Preparing to clone Securakey to T55x7 with raw hex"); + // EM4305 + if (em) { + blocks[0] = EM4305_SECURAKEY_CONFIG_BLOCK; + snprintf(cardtype, sizeof(cardtype), "EM4305/4469"); + } + + PrintAndLogEx(INFO, "Preparing to clone Securakey to " _YELLOW_("%s") " with raw hex", cardtype); print_blocks(blocks, ARRAYLEN(blocks)); - int res = clone_t55xx_tag(blocks, ARRAYLEN(blocks)); + int res; + if (em) { + res = em4x05_clone_tag(blocks, ARRAYLEN(blocks), 0, false); + } else { + res = clone_t55xx_tag(blocks, ARRAYLEN(blocks)); + } PrintAndLogEx(SUCCESS, "Done"); PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf securakey reader`") " to verify"); return res; From 3de85604561cb8c990132fe79b28158485c36ff5 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Wed, 25 Nov 2020 16:27:54 +0100 Subject: [PATCH 28/52] lf pyramid - now uses cliparser, contiouos mode, and clone to EM (untested) --- client/src/cmdlfpyramid.c | 166 +++++++++++++++++++++++--------------- 1 file changed, 101 insertions(+), 65 deletions(-) diff --git a/client/src/cmdlfpyramid.c b/client/src/cmdlfpyramid.c index aba305b5d..1973bad49 100644 --- a/client/src/cmdlfpyramid.c +++ b/client/src/cmdlfpyramid.c @@ -15,7 +15,6 @@ #include #include #include - #include "commonutil.h" // ARRAYLEN #include "cmdparser.h" // command_t #include "comms.h" @@ -27,43 +26,11 @@ #include "lfdemod.h" // parityTest #include "crc.h" #include "cmdlft55xx.h" // verifywrite +#include "cliparser.h" +#include "cmdlfem4x05.h" // EM Defines static int CmdHelp(const char *Cmd); -static int usage_lf_pyramid_clone(void) { - PrintAndLogEx(NORMAL, "clone a Farpointe/Pyramid tag to a T55x7 or Q5/T5555 tag."); - PrintAndLogEx(NORMAL, "The facility-code is 8-bit and the card number is 16-bit. Larger values are truncated. "); - PrintAndLogEx(NORMAL, "Currently only works on 26bit"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Usage: lf pyramid clone [h] [Q5]"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h : this help"); - PrintAndLogEx(NORMAL, " : 8-bit value facility code"); - PrintAndLogEx(NORMAL, " : 16-bit value card number"); - PrintAndLogEx(NORMAL, " Q5 : optional - specify writing to Q5/T5555 tag"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" lf pyramid clone 123 11223")); - return PM3_SUCCESS; -} - -static int usage_lf_pyramid_sim(void) { - PrintAndLogEx(NORMAL, "Enables simulation of Farpointe/Pyramid card with specified card number."); - PrintAndLogEx(NORMAL, "Simulation runs until the button is pressed or another USB command is issued."); - PrintAndLogEx(NORMAL, "The facility-code is 8-bit and the card number is 16-bit. Larger values are truncated."); - PrintAndLogEx(NORMAL, "Currently work only on 26bit"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Usage: lf pyramid sim [h] "); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h : this help"); - PrintAndLogEx(NORMAL, " : 8-bit value facility code"); - PrintAndLogEx(NORMAL, " : 16-bit value card number"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" lf pyramid sim 123 11223")); - return PM3_SUCCESS; -} - //Pyramid Prox demod - FSK RF/50 with preamble of 0000000000000001 (always a 128 bit data stream) //print full Farpointe Data/Pyramid Prox ID and some bit format details if found int demodPyramid(bool verbose) { @@ -215,26 +182,71 @@ static int CmdPyramidDemod(const char *Cmd) { return demodPyramid(true); } -static int CmdPyramidRead(const char *Cmd) { - (void)Cmd; // Cmd is not used so far - lf_read(false, 15000); - return demodPyramid(true); +static int CmdPyramidReader(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf pyramid reader", + "read a Farpointe/Pyramid tag", + "lf pyramid reader -@ -> continuous reader mode" + ); + + void *argtable[] = { + arg_param_begin, + arg_lit0("@", NULL, "optional - continuous reader mode"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + bool cm = arg_get_lit(ctx, 1); + CLIParserFree(ctx); + + do { + lf_read(false, 15000); + demodPyramid(true); + } while (cm && !kbd_enter_pressed()); + + return PM3_SUCCESS; } static int CmdPyramidClone(const char *Cmd) { - char cmdp = tolower(param_getchar(Cmd, 0)); - if (strlen(Cmd) == 0 || cmdp == 'h') return usage_lf_pyramid_clone(); - uint32_t facilitycode = 0, cardnumber = 0, fc = 0, cn = 0; - if (sscanf(Cmd, "%u %u", &fc, &cn) != 2) return usage_lf_pyramid_clone(); + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf pyramid clone", + "clone a Farpointe/Pyramid tag to a T55x7, Q5/T5555 or EM4305/4469 tag.\n" + "The facility-code is 8-bit and the card number is 16-bit. Larger values are truncated.\n" + "Currently only works on 26bit", + "lf pyramid clone --fc 123 --cn 11223\n" + "lf pyramid clone --fc 123 --cn 11223 --q5 -> encode for Q5/T5555 tag\n" + "lf pyramid clone --fc 123 --cn 11223 --em -> encode for EM4305/4469" + ); + + void *argtable[] = { + arg_param_begin, + arg_u64_1(NULL,"fc", "", "8-bit value facility code"), + arg_u64_1(NULL, "cn", "", "16-bit value card number"), + arg_lit0(NULL, "q5", "optional - specify writing to Q5/T5555 tag"), + arg_lit0(NULL, "em", "optional - specify writing to EM4305/4469 tag"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + uint32_t fc = arg_get_u32_def(ctx, 1, 0); + uint32_t cn = arg_get_u32_def(ctx, 2, 0); + bool q5 = arg_get_lit(ctx, 3); + bool em = arg_get_lit(ctx, 4); + CLIParserFree(ctx); + + if (q5 && em) { + PrintAndLogEx(FAILED, "Can't specify both Q5 and EM4305 at the same time"); + return PM3_EINVARG; + } + uint32_t blocks[5]; uint8_t *bs = calloc(128, sizeof(uint8_t)); if (bs == NULL) { return PM3_EMALLOC; } - facilitycode = (fc & 0x000000FF); - cardnumber = (cn & 0x0000FFFF); + uint32_t facilitycode = (fc & 0x000000FF); + uint32_t cardnumber = (cn & 0x0000FFFF); if (getPyramidBits(facilitycode, cardnumber, bs) != PM3_SUCCESS) { PrintAndLogEx(ERR, "Error with tag bitstream generation."); @@ -243,11 +255,18 @@ static int CmdPyramidClone(const char *Cmd) { //Pyramid - compat mode, FSK2a, data rate 50, 4 data blocks blocks[0] = T55x7_MODULATION_FSK2a | T55x7_BITRATE_RF_50 | 4 << T55x7_MAXBLOCK_SHIFT; + char cardtype[16] = {"T55x7"}; // Q5 - bool q5 = tolower(param_getchar(Cmd, 2)) == 'q'; - if (q5) + if (q5) { blocks[0] = T5555_FIXED | T5555_MODULATION_FSK2 | T5555_INVERT_OUTPUT | T5555_SET_BITRATE(50) | 4 << T5555_MAXBLOCK_SHIFT; + snprintf(cardtype, sizeof(cardtype), "Q5/T5555"); + } + // EM4305 + if (em) { + blocks[0] = EM4305_PYRAMID_CONFIG_BLOCK; + snprintf(cardtype, sizeof(cardtype), "EM4305/4469"); + } blocks[1] = bytebits_to_byte(bs, 32); blocks[2] = bytebits_to_byte(bs + 32, 32); @@ -256,36 +275,53 @@ static int CmdPyramidClone(const char *Cmd) { free(bs); - PrintAndLogEx(INFO, "Preparing to clone Farpointe/Pyramid to " _YELLOW_("%s") " with Facility Code: %u, Card Number: %u", (q5) ? "Q5/T5555" : "T55x7", facilitycode, cardnumber); + PrintAndLogEx(INFO, "Preparing to clone Farpointe/Pyramid to " _YELLOW_("%s") " with Facility Code: %u, Card Number: %u", cardtype, facilitycode, cardnumber); print_blocks(blocks, ARRAYLEN(blocks)); - int res = clone_t55xx_tag(blocks, ARRAYLEN(blocks)); + int res; + if (em) { + res = em4x05_clone_tag(blocks, ARRAYLEN(blocks), 0, false); + } else { + res = clone_t55xx_tag(blocks, ARRAYLEN(blocks)); + } PrintAndLogEx(SUCCESS, "Done"); - PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf pyramid read`") " to verify"); + PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf pyramid reader`") " to verify"); return res; } static int CmdPyramidSim(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf pyramid sim", + "Enables simulation of Farpointe/Pyramid card with specified card number.\n" + "Simulation runs until the button is pressed or another USB command is issued.\n" + "The facility-code is 8-bit and the card number is 16-bit. Larger values are truncated.\n" + "Currently work only on 26bit", + "lf pyramid sim --fc 123 --cn 1337" + ); - char cmdp = tolower(param_getchar(Cmd, 0)); - if (strlen(Cmd) == 0 || cmdp == 'h') return usage_lf_pyramid_sim(); + void *argtable[] = { + arg_param_begin, + arg_u64_1(NULL,"fc", "", "8-bit value facility code"), + arg_u64_1(NULL, "cn", "", "16-bit value card number"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + uint32_t fc = arg_get_u32_def(ctx, 1, 0); + uint32_t cn = arg_get_u32_def(ctx, 2, 0); + CLIParserFree(ctx); + + uint32_t facilitycode = (fc & 0x000000FF); + uint32_t cardnumber = (cn & 0x0000FFFF); - uint32_t facilitycode = 0, cardnumber = 0, fc = 0, cn = 0; uint8_t bs[128]; memset(bs, 0x00, sizeof(bs)); - - if (sscanf(Cmd, "%u %u", &fc, &cn) != 2) return usage_lf_pyramid_sim(); - - facilitycode = (fc & 0x000000FF); - cardnumber = (cn & 0x0000FFFF); - if (getPyramidBits(facilitycode, cardnumber, bs) != PM3_SUCCESS) { PrintAndLogEx(ERR, "Error with tag bitstream generation."); return PM3_ESOFT; } - PrintAndLogEx(SUCCESS, "Simulating Farpointe/Pyramid - Facility Code: %u, CardNumber: %u", facilitycode, cardnumber); + PrintAndLogEx(SUCCESS, "Simulating Farpointe/Pyramid - Facility Code: " _YELLOW_("%u") ", CardNumber: " _YELLOW_("%u"), facilitycode, cardnumber); // Pyramid uses: fcHigh: 10, fcLow: 8, clk: 50, invert: 0 lf_fsksim_t *payload = calloc(1, sizeof(lf_fsksim_t) + sizeof(bs)); @@ -309,11 +345,11 @@ static int CmdPyramidSim(const char *Cmd) { } static command_t CommandTable[] = { - {"help", CmdHelp, AlwaysAvailable, "this help"}, - {"demod", CmdPyramidDemod, AlwaysAvailable, "demodulate a Pyramid FSK tag from the GraphBuffer"}, - {"read", CmdPyramidRead, IfPm3Lf, "attempt to read and extract tag data"}, - {"clone", CmdPyramidClone, IfPm3Lf, "clone pyramid tag to T55x7 or Q5/T5555"}, - {"sim", CmdPyramidSim, IfPm3Lf, "simulate pyramid tag"}, + {"help", CmdHelp, AlwaysAvailable, "this help"}, + {"demod", CmdPyramidDemod, AlwaysAvailable, "demodulate a Pyramid FSK tag from the GraphBuffer"}, + {"reader", CmdPyramidReader, IfPm3Lf, "attempt to read and extract tag data"}, + {"clone", CmdPyramidClone, IfPm3Lf, "clone pyramid tag to T55x7 or Q5/T5555"}, + {"sim", CmdPyramidSim, IfPm3Lf, "simulate pyramid tag"}, {NULL, NULL, NULL, NULL} }; From 2b34c64ddeb5780630eba74d91ef76ec163415bd Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Wed, 25 Nov 2020 19:28:27 +0100 Subject: [PATCH 29/52] fix coverity 226302 --- client/src/cmdlfvisa2000.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/cmdlfvisa2000.c b/client/src/cmdlfvisa2000.c index 04e16e727..ee145e2b9 100644 --- a/client/src/cmdlfvisa2000.c +++ b/client/src/cmdlfvisa2000.c @@ -207,7 +207,7 @@ static int CmdVisa2kClone(const char *Cmd) { blocks[2] = id; blocks[3] = (visa_parity(id) << 4) | visa_chksum(id); - PrintAndLogEx(INFO, "Preparing to clone Visa2000 to " _YELLOW_("%s") " with CardId: %"PRIu64, cardtype, id); + PrintAndLogEx(INFO, "Preparing to clone Visa2000 to " _YELLOW_("%s") " with CardId: %"PRIu32, cardtype, id); print_blocks(blocks, ARRAYLEN(blocks)); int res; From 0d1760ad04adfefa52a1d3115f515264decff682 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Wed, 25 Nov 2020 19:42:45 +0100 Subject: [PATCH 30/52] try to please coverity --- client/src/emv/emvcore.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/client/src/emv/emvcore.c b/client/src/emv/emvcore.c index 913316725..0bcd547b1 100644 --- a/client/src/emv/emvcore.c +++ b/client/src/emv/emvcore.c @@ -778,7 +778,7 @@ int trDDA(EMVCommandChannel channel, bool decodeTLV, struct tlvdb *tlv) { if (sdad_tlv) { PrintAndLogEx(INFO, "* * Got Signed Dynamic Application Data (9F4B) form GPO. Maybe fDDA..."); - const struct tlvdb *atc_db = emv_pki_recover_atc_ex(icc_pk, tlv, true); + struct tlvdb *atc_db = emv_pki_recover_atc_ex(icc_pk, tlv, true); if (!atc_db) { PrintAndLogEx(ERR, "Error: Can't recover IDN (ICC Dynamic Number)"); emv_pk_free(pk); @@ -804,9 +804,10 @@ int trDDA(EMVCommandChannel channel, bool decodeTLV, struct tlvdb *tlv) { emv_pk_free(pk); emv_pk_free(issuer_pk); emv_pk_free(icc_pk); - atc_db = NULL; + tlvdb_free(atc_db); return 9; } + } else { struct tlvdb *dac_db = emv_pki_recover_dac(issuer_pk, tlv, sda_tlv); if (dac_db) { From 48b493a77a471a1bf07471caf2b88737d3156e00 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Wed, 25 Nov 2020 19:48:10 +0100 Subject: [PATCH 31/52] lf ti read -> converted to cliparser but hard to test/verify these commands. Most likely the continuous mode doesnt work because of impl of calling wo waiting --- client/src/cmdlfti.c | 39 ++++++++++++++++++++++++++++----------- 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/client/src/cmdlfti.c b/client/src/cmdlfti.c index dc3aeddf1..e2d7716d2 100644 --- a/client/src/cmdlfti.c +++ b/client/src/cmdlfti.c @@ -8,10 +8,10 @@ // Low frequency TI commands //----------------------------------------------------------------------------- +#include "cmdlfti.h" #include #include #include - #include "cmdparser.h" // command_t #include "commonutil.h" #include "comms.h" @@ -19,7 +19,7 @@ #include "ui.h" #include "proxgui.h" #include "graph.h" -#include "cmdlfti.h" +#include "cliparser.h" static int CmdHelp(const char *Cmd); @@ -277,10 +277,27 @@ static int CmdTIDemod(const char *Cmd) { } // read a TI tag and return its ID -static int CmdTIRead(const char *Cmd) { - (void)Cmd; // Cmd is not used so far - clearCommandBuffer(); - SendCommandNG(CMD_LF_TI_READ, NULL, 0); +static int CmdTIReader(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf ti reader", + "read a TI tag", + "lf ti reader -@ -> continuous reader mode" + ); + + void *argtable[] = { + arg_param_begin, + arg_lit0("@", NULL, "optional - continuous reader mode"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + bool cm = arg_get_lit(ctx, 1); + CLIParserFree(ctx); + + do { + clearCommandBuffer(); + SendCommandNG(CMD_LF_TI_READ, NULL, 0); + } while (cm && !kbd_enter_pressed()); + return PM3_SUCCESS; } @@ -300,15 +317,15 @@ static int CmdTIWrite(const char *Cmd) { clearCommandBuffer(); SendCommandMIX(CMD_LF_TI_WRITE, arg0, arg1, arg2, NULL, 0); PrintAndLogEx(SUCCESS, "Done"); - PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf ti read`") " to verify"); + PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf ti reader`") " to verify"); return PM3_SUCCESS; } static command_t CommandTable[] = { - {"help", CmdHelp, AlwaysAvailable, "This help"}, - {"demod", CmdTIDemod, AlwaysAvailable, "Demodulate raw bits for TI-type LF tag from the GraphBuffer"}, - {"read", CmdTIRead, IfPm3Lf, "Read and decode a TI 134 kHz tag"}, - {"write", CmdTIWrite, IfPm3Lf, "Write new data to a r/w TI 134 kHz tag"}, + {"help", CmdHelp, AlwaysAvailable, "This help"}, + {"demod", CmdTIDemod, AlwaysAvailable, "Demodulate raw bits for TI-type LF tag from the GraphBuffer"}, + {"reader", CmdTIReader, IfPm3Lf, "Read and decode a TI 134 kHz tag"}, + {"write", CmdTIWrite, IfPm3Lf, "Write new data to a r/w TI 134 kHz tag"}, {NULL, NULL, NULL, NULL} }; From 6b3f12ffe99fdc94bde7e0bedd96de744c5f4393 Mon Sep 17 00:00:00 2001 From: tcprst Date: Wed, 25 Nov 2020 14:02:52 -0500 Subject: [PATCH 32/52] hf iclass dump - now uses cliparser --- client/src/cmdhficlass.c | 222 +++++++++++++++++++-------------------- doc/cheatsheet.md | 15 ++- 2 files changed, 117 insertions(+), 120 deletions(-) diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index 284317a47..a3eb00e55 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -71,27 +71,6 @@ static int usage_hf_iclass_sim(void) { PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; } -static int usage_hf_iclass_dump(void) { - PrintAndLogEx(NORMAL, "Dump all memory from a iCLASS tag\n"); - PrintAndLogEx(NORMAL, "Usage: hf iclass dump f k c [e|r|v]\n"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h : Show this help"); - PrintAndLogEx(NORMAL, " f : specify a filename to save dump to"); - PrintAndLogEx(NORMAL, " k : access Key as 16 hex symbols or 1 hex to select key from memory OR NR/MAC for replay"); - PrintAndLogEx(NORMAL, " c : credit key as 16 hex symbols or 1 hex to select key from memory"); - PrintAndLogEx(NORMAL, " e : elite computations applied to key"); - PrintAndLogEx(NORMAL, " r : raw, the key is interpreted as raw block 3/4"); - PrintAndLogEx(NORMAL, " n : replay of NR/MAC"); - PrintAndLogEx(NORMAL, " v : verbose output"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_("\thf iclass dump k 001122334455667B")); - PrintAndLogEx(NORMAL, _YELLOW_("\thf iclass dump k AAAAAAAAAAAAAAAA c 001122334455667B")); - PrintAndLogEx(NORMAL, _YELLOW_("\thf iclass dump k AAAAAAAAAAAAAAAA e")); - PrintAndLogEx(NORMAL, _YELLOW_("\thf iclass dump k 0")); - PrintAndLogEx(NORMAL, ""); - return PM3_SUCCESS; -} static int usage_hf_iclass_restore(void) { PrintAndLogEx(NORMAL, "Restore data from dumpfile onto a iCLASS tag\n"); PrintAndLogEx(NORMAL, "Usage: hf iclass restore f b l k c e|r\n"); @@ -1359,104 +1338,115 @@ static bool select_only(uint8_t *CSN, uint8_t *CCNR, bool verbose) { } static int CmdHFiClassDump(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf iclass dump", + "Dump all memory from a iCLASS tag", + "hf iclass dump -k 001122334455667B\n" + "hf iclass dump -k AAAAAAAAAAAAAAAA --credit 001122334455667B\n" + "hf iclass dump -k AAAAAAAAAAAAAAAA --elite\n" + "hf iclass dump --ki 0\n" + "hf iclass dump --ki 0 --ci 2"); - uint8_t KEY[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - uint8_t CreditKEY[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - uint8_t keyNbr = 0; - uint8_t dataLen = 0; - uint8_t app_limit1 = 0, app_limit2 = 0; - uint8_t fileNameLen = 0; + void *argtable[] = { + arg_param_begin, + arg_str0("f", "file", "", "filename to save dump to"), + arg_str0("k", "key", "", "debit key as 16 hex symbols OR NR/MAC for replay"), + arg_int0(NULL, "ki", "", "debit key index to select key from memory 'hf iclass managekeys'"), + arg_str0(NULL, "credit", "", "credit key as 16 hex symbols"), + arg_int0(NULL, "ci", "", "credit key index to select key from memory 'hf iclass managekeys'"), + arg_lit0(NULL, "elite", "elite computations applied to key"), + arg_lit0(NULL, "raw", "raw, the key is interpreted as raw block 3/4"), + arg_lit0(NULL, "nr", "replay of NR/MAC"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + int fnlen = 0; char filename[FILE_PATH_SIZE] = {0}; - char tempStr[50] = {0}; - bool have_credit_key = false; - bool elite = false; - bool rawkey = false; - bool use_replay = false; - bool errors = false; - bool auth = false; - uint8_t cmdp = 0; + CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); - while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { - switch (tolower(param_getchar(Cmd, cmdp))) { - case 'h': - return usage_hf_iclass_dump(); - case 'c': - auth = true; - have_credit_key = true; - dataLen = param_getstr(Cmd, cmdp + 1, tempStr, sizeof(tempStr)); - if (dataLen == 16) { - errors = param_gethex(tempStr, 0, CreditKEY, dataLen); - } else if (dataLen == 1) { - keyNbr = param_get8(Cmd, cmdp + 1); - if (keyNbr < ICLASS_KEYS_MAX) { - memcpy(CreditKEY, iClass_Key_Table[keyNbr], 8); - PrintAndLogEx(INFO, "AA2 (credit) index %u", keyNbr); - } else { - PrintAndLogEx(WARNING, "\nERROR: Credit KeyNbr is invalid\n"); - errors = true; - } - } else { - PrintAndLogEx(WARNING, "\nERROR: Credit Key is incorrect length\n"); - errors = true; - } - cmdp += 2; - break; - case 'e': - PrintAndLogEx(SUCCESS, "Using " _YELLOW_("elite algo")); - elite = true; - cmdp++; - break; - case 'f': - fileNameLen = param_getstr(Cmd, cmdp + 1, filename, sizeof(filename)); - if (fileNameLen < 1) { - PrintAndLogEx(WARNING, "no filename found after f"); - errors = true; - } - cmdp += 2; - break; - case 'k': - auth = true; - dataLen = param_getstr(Cmd, cmdp + 1, tempStr, sizeof(tempStr)); - if (dataLen == 16) { - errors = param_gethex(tempStr, 0, KEY, dataLen); - } else if (dataLen == 1) { - keyNbr = param_get8(Cmd, cmdp + 1); - if (keyNbr < ICLASS_KEYS_MAX) { - memcpy(KEY, iClass_Key_Table[keyNbr], 8); - PrintAndLogEx(INFO, "AA1 (debit) index %u", keyNbr); - } else { - PrintAndLogEx(WARNING, "\nERROR: Credit KeyNbr is invalid\n"); - errors = true; - } - } else { - PrintAndLogEx(WARNING, "\nERROR: Credit Key is incorrect length\n"); - errors = true; - } - cmdp += 2; - break; - case 'r': - PrintAndLogEx(SUCCESS, "Using " _YELLOW_("raw mode")); - rawkey = true; - cmdp++; - break; - case 'n': - PrintAndLogEx(SUCCESS, "Using " _YELLOW_("replay NR/MAC mode")); - use_replay = true; - cmdp++; - break; - default: - PrintAndLogEx(WARNING, "Unknown parameter '%c'\n", param_getchar(Cmd, cmdp)); - errors = true; - break; + int key_len = 0; + uint8_t key[8] = {0}; + bool have_debit_key = false; + + CLIGetHexWithReturn(ctx, 2, key, &key_len); + + int deb_key_nr = arg_get_int_def(ctx, 3, -1); + + if (key_len > 0 && deb_key_nr >= 0) { + PrintAndLogEx(ERR, "Please specify debit key or index, not both"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + + if (key_len > 0) { + have_debit_key = true; + if (key_len != 8) { + PrintAndLogEx(ERR, "Debit key is incorrect length"); + CLIParserFree(ctx); + return PM3_EINVARG; } } - if ((use_replay + rawkey + elite) > 1) { - PrintAndLogEx(FAILED, "Can not use a combo of 'e', 'r', 'n'"); - errors = true; + if (deb_key_nr >= 0) { + if (deb_key_nr < ICLASS_KEYS_MAX) { + have_debit_key = true; + memcpy(key, iClass_Key_Table[deb_key_nr], 8); + PrintAndLogEx(INFO, "AA1 (debit) index %u", deb_key_nr); + } else { + PrintAndLogEx(ERR, "Key number is invalid"); + CLIParserFree(ctx); + return PM3_EINVARG; + } } - if (errors) return usage_hf_iclass_dump(); + int credit_key_len = 0; + uint8_t credit_key[8] = {0}; + bool have_credit_key = false; + + CLIGetHexWithReturn(ctx, 4, credit_key, &credit_key_len); + + int credit_key_nr = arg_get_int_def(ctx, 5, -1); + + if (credit_key_len > 0 && credit_key_nr >= 0) { + PrintAndLogEx(ERR, "Please specify credit key or index, not both"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + + if (credit_key_len > 0) { + have_credit_key = true; + if (key_len != 8) { + PrintAndLogEx(ERR, "Credit key is incorrect length"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + } + + if (credit_key_nr >= 0) { + if (credit_key_nr < ICLASS_KEYS_MAX) { + have_credit_key = true; + memcpy(key, iClass_Key_Table[credit_key_nr], 8); + PrintAndLogEx(INFO, "AA2 (credit) index %u", credit_key_nr); + } else { + PrintAndLogEx(ERR, "Key number is invalid"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + } + + bool elite = arg_get_lit(ctx, 6); + bool rawkey = arg_get_lit(ctx, 7); + bool use_replay = arg_get_lit(ctx, 8); + + CLIParserFree(ctx); + + if ((use_replay + rawkey + elite) > 1) { + PrintAndLogEx(FAILED, "Can not use a combo of 'e', 'r', 'n'"); + return PM3_EINVARG; + } + + uint8_t app_limit1 = 0, app_limit2 = 0; uint32_t flags = (FLAG_ICLASS_READER_INIT | FLAG_ICLASS_READER_CLEARTRACE); @@ -1511,11 +1501,11 @@ static int CmdHFiClassDump(const char *Cmd) { if (pagemap == PICOPASS_NON_SECURE_PAGEMODE) { PrintAndLogEx(INFO, "Dumping all available memory, block 3 - %u (0x%02x)", app_limit1, app_limit1); - if (auth) { + if (have_debit_key) { PrintAndLogEx(INFO, "No keys needed, ignoring user supplied key"); } } else { - if (auth == false) { + if (have_debit_key == false) { PrintAndLogEx(FAILED, "Run command with keys"); return PM3_ESOFT; } @@ -1528,10 +1518,10 @@ static int CmdHFiClassDump(const char *Cmd) { .req.use_credit_key = false, .req.use_replay = use_replay, .req.send_reply = true, - .req.do_auth = auth, + .req.do_auth = have_debit_key, .end_block = app_limit1, }; - memcpy(payload.req.key, KEY, 8); + memcpy(payload.req.key, key, 8); // tags configured for NON SECURE PAGE, acts different if (pagemap == PICOPASS_NON_SECURE_PAGEMODE) { @@ -1606,7 +1596,7 @@ static int CmdHFiClassDump(const char *Cmd) { if (have_credit_key && pagemap != 0x01) { // AA2 authenticate credit key - memcpy(payload.req.key, CreditKEY, 8); + memcpy(payload.req.key, credit_key, 8); payload.req.use_credit_key = true; payload.start_block = app_limit1 + 1; diff --git a/doc/cheatsheet.md b/doc/cheatsheet.md index 9699fabf0..c466a4784 100644 --- a/doc/cheatsheet.md +++ b/doc/cheatsheet.md @@ -62,9 +62,16 @@ Dump iCLASS card contents ``` Options --- -k : *Access Key as 16 hex symbols or 1 hex to select key from memory +-f, --file filename to save dump to +-k, --key debit key as 16 hex symbols OR NR/MAC for replay + --ki debit key index to select key from memory 'hf iclass managekeys' + --credit credit key as 16 hex symbols + --ci credit key index to select key from memory 'hf iclass managekeys' + --elite elite computations applied to key + --raw raw, the key is interpreted as raw block 3/4 + --nr replay of NR/MAC -m3 --> hf iclass dump k 0 +pm3 --> hf iclass dump --ki 0 ``` Read iCLASS Block @@ -161,7 +168,7 @@ pm3 --> hf iclass sim 3 Simulate iCLASS Sequence ``` -pm3 --> hf iclass dump k 0 +pm3 --> hf iclass dump --ki 0 pm3 --> hf iclass eload -f hf-iclass-db883702f8ff12e0.bin pm3 --> hf iclass sim 3 ``` @@ -177,7 +184,7 @@ e : If 'e' is specified, elite computations applied to key pm3 --> hf iclass sim 2 pm3 --> hf iclass loclass -f iclass_mac_attack.bin pm3 --> hf iclass managekeys n 7 k -pm3 --> hf iclass dump k 7 e +pm3 --> hf iclass dump --ki 7 --elite ``` Verify custom iCLASS key From 88b7efe69a69d46f96058f3a36f67072509d78a2 Mon Sep 17 00:00:00 2001 From: tcprst Date: Wed, 25 Nov 2020 18:00:32 -0500 Subject: [PATCH 33/52] hf iclass restore - now use cliparser --- armsrc/iclass.c | 4 +- client/src/cmdhficlass.c | 193 ++++++++++++++++----------------------- 2 files changed, 80 insertions(+), 117 deletions(-) diff --git a/armsrc/iclass.c b/armsrc/iclass.c index 86d8094b7..f6d338f9c 100644 --- a/armsrc/iclass.c +++ b/armsrc/iclass.c @@ -1999,10 +1999,10 @@ void iClass_Restore(iclass_restore_req_t *msg) { // data + mac if (iclass_writeblock_ext(item.blockno, item.data, mac, use_mac)) { - Dbprintf("Write block [%02x] " _GREEN_("successful"), item.blockno); + Dbprintf("Write block [%02d] " _GREEN_("successful"), item.blockno); written++; } else { - Dbprintf("Write block [%02x] " _RED_("failed"), item.blockno); + Dbprintf("Write block [%02d] " _RED_("failed"), item.blockno); } } diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index a3eb00e55..f647f6aeb 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -71,25 +71,6 @@ static int usage_hf_iclass_sim(void) { PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; } -static int usage_hf_iclass_restore(void) { - PrintAndLogEx(NORMAL, "Restore data from dumpfile onto a iCLASS tag\n"); - PrintAndLogEx(NORMAL, "Usage: hf iclass restore f b l k c e|r\n"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h : Show this help"); - PrintAndLogEx(NORMAL, " f : specify a filename to restore"); - PrintAndLogEx(NORMAL, " b : The first block to restore as 2 hex symbols"); - PrintAndLogEx(NORMAL, " l : The last block to restore as 2 hex symbols"); - PrintAndLogEx(NORMAL, " k : Access key as 16 hex symbols or 1 hex to select key from memory"); - PrintAndLogEx(NORMAL, " c : If 'c' is specified, the key set is assumed to be the credit key\n"); - PrintAndLogEx(NORMAL, " e : If 'e' is specified, elite computations applied to key"); - PrintAndLogEx(NORMAL, " r : If 'r' is specified, no computations applied to key (raw)"); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_("\thf iclass restore f hf-iclass-AA162D30F8FF12F1-dump.bin b 06 l 1A k 1122334455667788 e")); - PrintAndLogEx(NORMAL, _YELLOW_("\thf iclass restore f hf-iclass-AA162D30F8FF12F1-dump b 05 l 19 k 0")); - PrintAndLogEx(NORMAL, _YELLOW_("\thf iclass restore f hf-iclass-AA162D30F8FF12F1-dump b 06 l 19 k 0 e")); - PrintAndLogEx(NORMAL, ""); - return PM3_SUCCESS; -} static int usage_hf_iclass_writeblock(void) { PrintAndLogEx(NORMAL, "Write data to a iCLASS tag\n"); PrintAndLogEx(NORMAL, "Usage: hf iclass wrbl b d k [c|e|r|v]\n"); @@ -1821,92 +1802,75 @@ static int CmdHFiClass_WriteBlock(const char *Cmd) { } static int CmdHFiClassRestore(const char *Cmd) { - char filename[FILE_PATH_SIZE] = { 0x00 }; - char tempStr[50] = {0}; - uint8_t KEY[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - uint8_t keyNbr = 0; - uint8_t fileNameLen = 0; - uint8_t startblock = 0; - uint8_t endblock = 0; - uint8_t dataLen = 0; - bool got_startblk = false, got_endblk = false; - bool use_credit_key = false; - bool elite = false; - bool rawkey = false; - bool errors = false; - bool verbose = false; - uint8_t cmdp = 0; + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf iclass restore", + "Restore data from dumpfile onto a iCLASS tag", + "hf iclass restore -f hf-iclass-AA162D30F8FF12F1-dump.bin --first 06 --last 1A -k 1122334455667788 --elite\n" + "hf iclass restore -f hf-iclass-AA162D30F8FF12F1-dump.bin --first 05 --last 19 --ki 0\n" + "hf iclass restore -f hf-iclass-AA162D30F8FF12F1-dump.bin --first 06 --last 19 --ki 0 --elite"); - while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { - switch (tolower(param_getchar(Cmd, cmdp))) { - case 'h': - return usage_hf_iclass_restore(); - case 'b': - startblock = param_get8ex(Cmd, cmdp + 1, 07, 16); - got_startblk = true; - cmdp += 2; - break; - case 'c': - PrintAndLogEx(SUCCESS, "Using " _YELLOW_("CREDIT")); - use_credit_key = true; - cmdp++; - break; - case 'e': - PrintAndLogEx(SUCCESS, "Using " _YELLOW_("elite algo")); - elite = true; - cmdp++; - break; - case 'f': - fileNameLen = param_getstr(Cmd, cmdp + 1, filename, sizeof(filename)); - if (fileNameLen < 1) { - PrintAndLogEx(WARNING, "No filename found after f"); - errors = true; - } - cmdp += 2; - break; - case 'k': - dataLen = param_getstr(Cmd, cmdp + 1, tempStr, sizeof(tempStr)); - if (dataLen == 16) { - errors = param_gethex(tempStr, 0, KEY, dataLen); - } else if (dataLen == 1) { - keyNbr = param_get8(Cmd, cmdp + 1); - if (keyNbr < ICLASS_KEYS_MAX) { - PrintAndLogEx(SUCCESS, "Using key[%d] " _GREEN_("%s"), keyNbr, sprint_hex(iClass_Key_Table[keyNbr], 8)); - memcpy(KEY, iClass_Key_Table[keyNbr], 8); - } else { - PrintAndLogEx(WARNING, "\nERROR: Credit KeyNbr is invalid\n"); - errors = true; - } - } else { - PrintAndLogEx(WARNING, "\nERROR: Credit Key is incorrect length\n"); - errors = true; - } - cmdp += 2; - break; - case 'l': - endblock = param_get8ex(Cmd, cmdp + 1, 07, 16); - got_endblk = true; - cmdp += 2; - break; - case 'r': - PrintAndLogEx(SUCCESS, "Using " _YELLOW_("raw mode")); - rawkey = true; - cmdp++; - break; - case 'v': - verbose = true; - cmdp++; - break; - default: - PrintAndLogEx(WARNING, "unknown parameter '%c'\n", param_getchar(Cmd, cmdp)); - errors = true; - break; - } + void *argtable[] = { + arg_param_begin, + arg_str1("f", "file", "", "specify a filename to restore"), + arg_str0("k", "key", "", "Access key as 16 hex symbols"), + arg_int0(NULL, "ki", "", "Key index to select key from memory 'hf iclass managekeys'"), + arg_int1(NULL, "first", "", "The first block number to restore as an integer"), + arg_int1(NULL, "last", "", "The last block number to restore as an integer"), + arg_lit0(NULL, "credit", "key is assumed to be the credit key"), + arg_lit0(NULL, "elite", "elite computations applied to key"), + arg_lit0(NULL, "raw", "no computations applied to key (raw)"), + arg_lit0("v", "verbose", "verbose output"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + int fnlen = 0; + char filename[FILE_PATH_SIZE] = {0}; + CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); + + int key_len = 0; + uint8_t key[8] = {0}; + + CLIGetHexWithReturn(ctx, 2, key, &key_len); + + int key_nr = arg_get_int_def(ctx, 3, -1); + + if (key_len > 0 && key_nr >= 0) { + PrintAndLogEx(ERR, "Please specify key or index, not both"); + CLIParserFree(ctx); + return PM3_EINVARG; } - if (got_endblk == false || got_startblk == false) - errors = true; - if (errors || cmdp < 8) return usage_hf_iclass_restore(); + if (key_len > 0) { + if (key_len != 8) { + PrintAndLogEx(ERR, "Key is incorrect length"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + } else if (key_nr >= 0) { + if (key_nr < ICLASS_KEYS_MAX) { + memcpy(key, iClass_Key_Table[key_nr], 8); + PrintAndLogEx(SUCCESS, "Using key[%d] " _GREEN_("%s"), key_nr, sprint_hex(iClass_Key_Table[key_nr], 8)); + } else { + PrintAndLogEx(ERR, "Key number is invalid"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + } else { + PrintAndLogEx(ERR, "Please specify a key or key index"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + + int startblock = arg_get_int_def(ctx, 4, 0); + int endblock = arg_get_int_def(ctx, 5, 0); + + bool use_credit_key = arg_get_lit(ctx, 6); + bool elite = arg_get_lit(ctx, 7); + bool rawkey = arg_get_lit(ctx, 8); + bool verbose = arg_get_lit(ctx, 9); + + CLIParserFree(ctx); if (rawkey + elite > 1) { PrintAndLogEx(FAILED, "Can not use both 'e', 'r'"); @@ -1954,7 +1918,7 @@ static int CmdHFiClassRestore(const char *Cmd) { payload->req.blockno = startblock; payload->req.send_reply = true; payload->req.do_auth = true; - memcpy(payload->req.key, KEY, 8); + memcpy(payload->req.key, key, 8); payload->item_cnt = (endblock - startblock + 1); @@ -1971,7 +1935,7 @@ static int CmdHFiClassRestore(const char *Cmd) { free(dump); if (verbose) { - PrintAndLogEx(INFO, "Preparing to restore block range 0x%02x..0x%02x", startblock, endblock); + PrintAndLogEx(INFO, "Preparing to restore block range %02d..%02d", startblock, endblock); PrintAndLogEx(INFO, "------+----------------------"); PrintAndLogEx(INFO, "block | data"); @@ -1979,7 +1943,7 @@ static int CmdHFiClassRestore(const char *Cmd) { for (uint8_t i = 0; i < payload->item_cnt; i++) { iclass_restore_item_t item = payload->blocks[i]; - PrintAndLogEx(INFO, " %02X | %s", item.blockno, sprint_hex_inrow(item.data, sizeof(item.data))); + PrintAndLogEx(INFO, " %02d | %s", item.blockno, sprint_hex_inrow(item.data, sizeof(item.data))); } } @@ -2313,9 +2277,9 @@ void printIclassDumpContents(uint8_t *iclass_dump, uint8_t startblock, uint8_t e int i = startblock; PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, " blk| data | ascii |lck| info"); + PrintAndLogEx(INFO, "blk | data | ascii |lck| info"); PrintAndLogEx(INFO, "----+-------------------------+----------+---+--------------"); - PrintAndLogEx(INFO, "0x00| " _GREEN_("%s") " | | CSN ", sprint_hex_ascii(iclass_dump, 8)); + PrintAndLogEx(INFO, " 00 | " _GREEN_("%s") " | | CSN ", sprint_hex_ascii(iclass_dump, 8)); if (i != 1) PrintAndLogEx(INFO, "...."); @@ -2368,14 +2332,14 @@ void printIclassDumpContents(uint8_t *iclass_dump, uint8_t startblock, uint8_t e s = info_nonks[i]; } - PrintAndLogEx(INFO, "0x%02X| %s | %s | %s ", i, sprint_hex_ascii(blk, 8), lockstr, s); + PrintAndLogEx(INFO, " %02d | %s | %s | %s ", i, sprint_hex_ascii(blk, 8), lockstr, s); } else { const char *info_ks[] = {"CSN", "Config", "E-purse", "Debit", "Credit", "AIA", "User"}; const char *s = info_ks[6]; if (i < 6) { s = info_ks[i]; } - PrintAndLogEx(INFO, "0x%02X| %s | %s | %s ", i, sprint_hex_ascii(blk, 8), lockstr, s); + PrintAndLogEx(INFO, " %02d | %s | %s | %s ", i, sprint_hex_ascii(blk, 8), lockstr, s); } i++; } @@ -2388,14 +2352,14 @@ static int CmdHFiClassView(const char *Cmd) { CLIParserInit(&ctx, "hf iclass view", "Print a iCLASS tag dump file", "hf iclass view -f hf-iclass-AA162D30F8FF12F1-dump.bin\n" - "hf iclass view --startblock 1 --file hf-iclass-AA162D30F8FF12F1-dump.bin\n"); + "hf iclass view --first 1 --file hf-iclass-AA162D30F8FF12F1-dump.bin\n"); void *argtable[] = { arg_param_begin, arg_str1("f", "file", "", "filename of dump"), - arg_int0("s", "startblock", "", "print from this block (default block6)"), - arg_int0("e", "endblock", "", "end printing at this block (default 0, ALL)"), - arg_lit0("v", "verbose", "verbose output"), + arg_int0(NULL, "first", "", "Begin printing from this block (default block6)"), + arg_int0(NULL, "last", "", "End printing at this block (default 0, ALL)"), + arg_lit0("v", "verbose", "verbose output"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -2420,8 +2384,7 @@ static int CmdHFiClassView(const char *Cmd) { if (verbose) { PrintAndLogEx(INFO, "File: " _YELLOW_("%s"), filename); PrintAndLogEx(INFO, "File size %zu bytes, file blocks %d (0x%x)", bytes_read, (uint16_t)(bytes_read >> 3), (uint16_t)(bytes_read >> 3)); - PrintAndLogEx(INFO, "Printing blocks from"); - PrintAndLogEx(INFO, "start " _YELLOW_("0x%02x") " end " _YELLOW_("0x%02x"), (startblock == 0) ? 6 : startblock, endblock); + PrintAndLogEx(INFO, "Printing blocks from: " _YELLOW_("%02d") " to: " _YELLOW_("%02d"), (startblock == 0) ? 6 : startblock, endblock); } PrintAndLogEx(NORMAL, ""); From f4b100b0684579646788f15f533f73b959407029 Mon Sep 17 00:00:00 2001 From: tcprst Date: Wed, 25 Nov 2020 18:42:22 -0500 Subject: [PATCH 34/52] show both decimal and hex for iclass blocks --- armsrc/iclass.c | 4 ++-- client/src/cmdhficlass.c | 34 ++++++++++++++++++---------------- 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/armsrc/iclass.c b/armsrc/iclass.c index f6d338f9c..5cb5d71ae 100644 --- a/armsrc/iclass.c +++ b/armsrc/iclass.c @@ -1999,10 +1999,10 @@ void iClass_Restore(iclass_restore_req_t *msg) { // data + mac if (iclass_writeblock_ext(item.blockno, item.data, mac, use_mac)) { - Dbprintf("Write block [%02d] " _GREEN_("successful"), item.blockno); + Dbprintf("Write block [%3d/0x%02X] " _GREEN_("successful"), item.blockno, item.blockno); written++; } else { - Dbprintf("Write block [%02d] " _RED_("failed"), item.blockno); + Dbprintf("Write block [%3d/0x%02X] " _RED_("failed"), item.blockno, item.blockno); } } diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index f647f6aeb..b55f2801c 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -1348,7 +1348,7 @@ static int CmdHFiClassDump(const char *Cmd) { int key_len = 0; uint8_t key[8] = {0}; - bool have_debit_key = false; + bool auth = false; CLIGetHexWithReturn(ctx, 2, key, &key_len); @@ -1361,7 +1361,7 @@ static int CmdHFiClassDump(const char *Cmd) { } if (key_len > 0) { - have_debit_key = true; + auth = true; if (key_len != 8) { PrintAndLogEx(ERR, "Debit key is incorrect length"); CLIParserFree(ctx); @@ -1371,7 +1371,7 @@ static int CmdHFiClassDump(const char *Cmd) { if (deb_key_nr >= 0) { if (deb_key_nr < ICLASS_KEYS_MAX) { - have_debit_key = true; + auth = true; memcpy(key, iClass_Key_Table[deb_key_nr], 8); PrintAndLogEx(INFO, "AA1 (debit) index %u", deb_key_nr); } else { @@ -1396,6 +1396,7 @@ static int CmdHFiClassDump(const char *Cmd) { } if (credit_key_len > 0) { + auth = true; have_credit_key = true; if (key_len != 8) { PrintAndLogEx(ERR, "Credit key is incorrect length"); @@ -1406,6 +1407,7 @@ static int CmdHFiClassDump(const char *Cmd) { if (credit_key_nr >= 0) { if (credit_key_nr < ICLASS_KEYS_MAX) { + auth = true; have_credit_key = true; memcpy(key, iClass_Key_Table[credit_key_nr], 8); PrintAndLogEx(INFO, "AA2 (credit) index %u", credit_key_nr); @@ -1482,11 +1484,11 @@ static int CmdHFiClassDump(const char *Cmd) { if (pagemap == PICOPASS_NON_SECURE_PAGEMODE) { PrintAndLogEx(INFO, "Dumping all available memory, block 3 - %u (0x%02x)", app_limit1, app_limit1); - if (have_debit_key) { + if (auth) { PrintAndLogEx(INFO, "No keys needed, ignoring user supplied key"); } } else { - if (have_debit_key == false) { + if (auth == false) { PrintAndLogEx(FAILED, "Run command with keys"); return PM3_ESOFT; } @@ -1499,7 +1501,7 @@ static int CmdHFiClassDump(const char *Cmd) { .req.use_credit_key = false, .req.use_replay = use_replay, .req.send_reply = true, - .req.do_auth = have_debit_key, + .req.do_auth = auth, .end_block = app_limit1, }; memcpy(payload.req.key, key, 8); @@ -1937,13 +1939,13 @@ static int CmdHFiClassRestore(const char *Cmd) { if (verbose) { PrintAndLogEx(INFO, "Preparing to restore block range %02d..%02d", startblock, endblock); - PrintAndLogEx(INFO, "------+----------------------"); - PrintAndLogEx(INFO, "block | data"); - PrintAndLogEx(INFO, "------+----------------------"); + PrintAndLogEx(INFO, "---------+----------------------"); + PrintAndLogEx(INFO, " block# | data"); + PrintAndLogEx(INFO, "---------+----------------------"); for (uint8_t i = 0; i < payload->item_cnt; i++) { iclass_restore_item_t item = payload->blocks[i]; - PrintAndLogEx(INFO, " %02d | %s", item.blockno, sprint_hex_inrow(item.data, sizeof(item.data))); + PrintAndLogEx(INFO, "%3d/0x%02X | %s", item.blockno, item.blockno, sprint_hex_inrow(item.data, sizeof(item.data))); } } @@ -2277,9 +2279,9 @@ void printIclassDumpContents(uint8_t *iclass_dump, uint8_t startblock, uint8_t e int i = startblock; PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, "blk | data | ascii |lck| info"); - PrintAndLogEx(INFO, "----+-------------------------+----------+---+--------------"); - PrintAndLogEx(INFO, " 00 | " _GREEN_("%s") " | | CSN ", sprint_hex_ascii(iclass_dump, 8)); + PrintAndLogEx(INFO, " block# | data | ascii |lck| info"); + PrintAndLogEx(INFO, "---------+-------------------------+----------+---+--------------"); + PrintAndLogEx(INFO, " 0/0x00 | " _GREEN_("%s") " | | CSN ", sprint_hex_ascii(iclass_dump, 8)); if (i != 1) PrintAndLogEx(INFO, "...."); @@ -2332,18 +2334,18 @@ void printIclassDumpContents(uint8_t *iclass_dump, uint8_t startblock, uint8_t e s = info_nonks[i]; } - PrintAndLogEx(INFO, " %02d | %s | %s | %s ", i, sprint_hex_ascii(blk, 8), lockstr, s); + PrintAndLogEx(INFO, "%3d/0x%02X | %s | %s | %s ", i, i, sprint_hex_ascii(blk, 8), lockstr, s); } else { const char *info_ks[] = {"CSN", "Config", "E-purse", "Debit", "Credit", "AIA", "User"}; const char *s = info_ks[6]; if (i < 6) { s = info_ks[i]; } - PrintAndLogEx(INFO, " %02d | %s | %s | %s ", i, sprint_hex_ascii(blk, 8), lockstr, s); + PrintAndLogEx(INFO, "%3d/0x%02X | %s | %s | %s ", i, i, sprint_hex_ascii(blk, 8), lockstr, s); } i++; } - PrintAndLogEx(INFO, "----+-------------------------+----------+---+--------------"); + PrintAndLogEx(INFO, "---------+-------------------------+----------+---+--------------"); PrintAndLogEx(NORMAL, ""); } From 5130bb24e204a7c18d87f894b51cce733c48bd44 Mon Sep 17 00:00:00 2001 From: tcprst Date: Wed, 25 Nov 2020 21:16:08 -0500 Subject: [PATCH 35/52] hf iclass rdbl - now use cliparser --- client/src/cmdhficlass.c | 181 +++++++++++++++++---------------------- doc/cheatsheet.md | 13 ++- 2 files changed, 87 insertions(+), 107 deletions(-) diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index b55f2801c..0ab48a161 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -91,25 +91,6 @@ static int usage_hf_iclass_writeblock(void) { PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; } -static int usage_hf_iclass_readblock(void) { - PrintAndLogEx(NORMAL, "Read a iCLASS block from tag\n"); - PrintAndLogEx(NORMAL, "Usage: hf iclass rdbl b k [c|e|r|v]\n"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h : Show this help"); - PrintAndLogEx(NORMAL, " b : The block number as 2 hex symbols"); - PrintAndLogEx(NORMAL, " k : access Key as 16 hex symbols or 1 hex to select key from memory OR NR/MAC for replay"); - PrintAndLogEx(NORMAL, " c : credit key assumed\n"); - PrintAndLogEx(NORMAL, " e : elite computations applied to key"); - PrintAndLogEx(NORMAL, " r : raw, no computations applied to key"); - PrintAndLogEx(NORMAL, " n : replay of NR/MAC"); - PrintAndLogEx(NORMAL, " v : verbose output"); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_("\thf iclass rdbl b 06 k 0011223344556677")); - PrintAndLogEx(NORMAL, _YELLOW_("\thf iclass rdbl b 1B k 0011223344556677 c")); - PrintAndLogEx(NORMAL, _YELLOW_("\thf iclass rdbl b 0A k 0")); - PrintAndLogEx(NORMAL, ""); - return PM3_SUCCESS; -} static int usage_hf_iclass_calc_newkey(void) { PrintAndLogEx(NORMAL, "Calculate new key for updating\n"); PrintAndLogEx(NORMAL, "Usage: hf iclass calc_newkey o n s [csn] e\n"); @@ -1373,7 +1354,7 @@ static int CmdHFiClassDump(const char *Cmd) { if (deb_key_nr < ICLASS_KEYS_MAX) { auth = true; memcpy(key, iClass_Key_Table[deb_key_nr], 8); - PrintAndLogEx(INFO, "AA1 (debit) index %u", deb_key_nr); + PrintAndLogEx(SUCCESS, "Using AA1 (debit) key[%d] " _GREEN_("%s"), deb_key_nr, sprint_hex(iClass_Key_Table[deb_key_nr], 8)); } else { PrintAndLogEx(ERR, "Key number is invalid"); CLIParserFree(ctx); @@ -1410,7 +1391,7 @@ static int CmdHFiClassDump(const char *Cmd) { auth = true; have_credit_key = true; memcpy(key, iClass_Key_Table[credit_key_nr], 8); - PrintAndLogEx(INFO, "AA2 (credit) index %u", credit_key_nr); + PrintAndLogEx(SUCCESS, "Using AA2 (credit) key[%d] " _GREEN_("%s"), credit_key_nr, sprint_hex(iClass_Key_Table[credit_key_nr], 8)); } else { PrintAndLogEx(ERR, "Key number is invalid"); CLIParserFree(ctx); @@ -2019,94 +2000,88 @@ static int iclass_read_block(uint8_t *KEY, uint8_t blockno, uint8_t keyType, boo } static int CmdHFiClass_ReadBlock(const char *Cmd) { - uint8_t blockno = 0; - uint8_t keyType = 0x88; //debit key - uint8_t KEY[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - uint8_t key_idx = 0; - uint8_t key_len = 0; - char tempStr[50] = {0}; - bool got_blockno = false; - bool elite = false; - bool rawkey = false; - bool use_replay = false; - bool errors = false; - bool auth = false; - bool verbose = false; - uint8_t cmdp = 0; + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf iclass rdbl", + "Read a iCLASS block from tag", + "hf iclass rdbl -b 6 -k 0011223344556677\n" + "hf iclass rdbl -b 27 -k 0011223344556677 --credit\n" + "hf iclass rdbl -b 10 --ki 0"); - while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { - switch (tolower(param_getchar(Cmd, cmdp))) { - case 'h': - return usage_hf_iclass_readblock(); - case 'b': - blockno = param_get8ex(Cmd, cmdp + 1, 7, 16); - got_blockno = true; - cmdp += 2; - break; - case 'c': - PrintAndLogEx(SUCCESS, "Using " _YELLOW_("KC credit")); - keyType = 0x18; - cmdp++; - break; - case 'e': - PrintAndLogEx(SUCCESS, "Using " _YELLOW_("elite algo")); - elite = true; - cmdp++; - break; - case 'k': - auth = true; - key_len = param_getstr(Cmd, cmdp + 1, tempStr, sizeof(tempStr)); - if (key_len == 16) { - errors = param_gethex(tempStr, 0, KEY, key_len); - } else if (key_len == 1) { - key_idx = param_get8(Cmd, cmdp + 1); - if (key_idx < ICLASS_KEYS_MAX) { - memcpy(KEY, iClass_Key_Table[key_idx], 8); - } else { - PrintAndLogEx(WARNING, "\nERROR: key index is invalid\n"); - errors = true; - } - } else { - PrintAndLogEx(WARNING, "\nERROR: incorrect key length\n"); - errors = true; - } - cmdp += 2; - break; - case 'r': - PrintAndLogEx(SUCCESS, "Using " _YELLOW_("raw mode")); - rawkey = true; - cmdp++; - break; - case 'n': - PrintAndLogEx(SUCCESS, "Using " _YELLOW_("replay NR/MAC mode")); - use_replay = true; - cmdp++; - break; - case 'v': - verbose = true; - cmdp++; - break; - default: - PrintAndLogEx(WARNING, "unknown parameter '%c'\n", param_getchar(Cmd, cmdp)); - errors = true; - break; - } + void *argtable[] = { + arg_param_begin, + arg_str0("k", "key", "", "Access key as 16 hex symbols"), + arg_int0(NULL, "ki", "", "Key index to select key from memory 'hf iclass managekeys'"), + arg_int1("b", "block", "", "The block number to read as an integer"), + arg_lit0(NULL, "credit", "key is assumed to be the credit key"), + arg_lit0(NULL, "elite", "elite computations applied to key"), + arg_lit0(NULL, "raw", "no computations applied to key (raw)"), + arg_lit0(NULL, "nr", "replay of NR/MAC"), + arg_lit0("v", "verbose", "verbose output"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + int key_len = 0; + uint8_t key[8] = {0}; + + CLIGetHexWithReturn(ctx, 1, key, &key_len); + + int key_nr = arg_get_int_def(ctx, 2, -1); + + if (key_len > 0 && key_nr >= 0) { + PrintAndLogEx(ERR, "Please specify key or index, not both"); + CLIParserFree(ctx); + return PM3_EINVARG; } - if (got_blockno == false) - errors = true; + + bool auth = false; + + if (key_len > 0) { + auth = true; + if (key_len != 8) { + PrintAndLogEx(ERR, "Key is incorrect length"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + } else if (key_nr >= 0) { + if (key_nr < ICLASS_KEYS_MAX) { + auth = true; + memcpy(key, iClass_Key_Table[key_nr], 8); + PrintAndLogEx(SUCCESS, "Using key[%d] " _GREEN_("%s"), key_nr, sprint_hex(iClass_Key_Table[key_nr], 8)); + } else { + PrintAndLogEx(ERR, "Key number is invalid"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + } else { + PrintAndLogEx(ERR, "Please specify a key or key index"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + + int blockno = arg_get_int_def(ctx, 3, 0); + + uint8_t keyType = 0x88; //debit key + if (arg_get_lit(ctx, 4)) { + PrintAndLogEx(SUCCESS, "Using " _YELLOW_("credit") " key"); + keyType = 0x18; //credit key + } + + bool elite = arg_get_lit(ctx, 5); + bool rawkey = arg_get_lit(ctx, 6); + bool use_replay = arg_get_lit(ctx, 7); + bool verbose = arg_get_lit(ctx, 8); + + CLIParserFree(ctx); if ((use_replay + rawkey + elite) > 1) { - PrintAndLogEx(FAILED, "Can not use a combo of 'e', 'r', 'n'"); - errors = true; + PrintAndLogEx(ERR, "Can not use a combo of 'e', 'r', 'n'"); + return PM3_EINVARG; } - if (errors) return usage_hf_iclass_readblock(); - if (verbose) { - if (key_len == 1) - PrintAndLogEx(SUCCESS, "Using key[%d] %s", key_idx, sprint_hex(KEY, 8)); - else - PrintAndLogEx(SUCCESS, "Using key %s", sprint_hex(KEY, 8)); + if (key_len > 0) + PrintAndLogEx(SUCCESS, "Using key %s", sprint_hex(key, 8)); } if (auth == false && verbose) { @@ -2115,7 +2090,7 @@ static int CmdHFiClass_ReadBlock(const char *Cmd) { } uint8_t data[8] = {0}; - int res = iclass_read_block(KEY, blockno, keyType, elite, rawkey, use_replay, verbose, auth, data); + int res = iclass_read_block(key, blockno, keyType, elite, rawkey, use_replay, verbose, auth, data); if (res != PM3_SUCCESS) return res; diff --git a/doc/cheatsheet.md b/doc/cheatsheet.md index c466a4784..3e092a173 100644 --- a/doc/cheatsheet.md +++ b/doc/cheatsheet.md @@ -78,10 +78,15 @@ Read iCLASS Block ``` Options --- -b : The block number as 2 hex symbols -k : Access Key as 16 hex symbols or 1 hex to select key from memory +-k, --key Access key as 16 hex symbols +-b, --block The block number to read as an integer + --ki Key index to select key from memory 'hf iclass managekeys' + --credit key is assumed to be the credit key + --elite elite computations applied to key + --raw no computations applied to key (raw) + --nr replay of NR/MAC -pm3 --> hf iclass rdbl b 7 k 0 +pm3 --> hf iclass rdbl -b 7 --ki 0 ``` Write to iCLASS Block @@ -149,7 +154,7 @@ pm3 --> hf iclass eload -f hf-iclass-db883702f8ff12e0.bin Clone iCLASS Legacy Sequence ``` -pm3 --> hf iclass rdbl b 7 k 0 +pm3 --> hf iclass rdbl -b 7 --ki 0 pm3 --> hf iclass wrbl b 7 d 6ce099fe7e614fd0 k 0 ``` From f723ed03869230d1bcfba1e35206f8b0db628bd7 Mon Sep 17 00:00:00 2001 From: tcprst Date: Thu, 26 Nov 2020 00:11:54 -0500 Subject: [PATCH 36/52] hf iclass wrbl - now use cliparser --- client/src/cmdhficlass.c | 192 ++++++++++++++++----------------------- doc/cheatsheet.md | 15 ++- 2 files changed, 88 insertions(+), 119 deletions(-) diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index 0ab48a161..6bb215344 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -71,26 +71,6 @@ static int usage_hf_iclass_sim(void) { PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; } -static int usage_hf_iclass_writeblock(void) { - PrintAndLogEx(NORMAL, "Write data to a iCLASS tag\n"); - PrintAndLogEx(NORMAL, "Usage: hf iclass wrbl b d k [c|e|r|v]\n"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h : Show this help"); - PrintAndLogEx(NORMAL, " b : The block number as 2 hex symbols"); - PrintAndLogEx(NORMAL, " d : set the Data to write as 16 hex symbols"); - PrintAndLogEx(NORMAL, " k : access Key as 16 hex symbols or 1 hex to select key from memory OR NR/MAC for replay"); - PrintAndLogEx(NORMAL, " c : credit key assumed\n"); - PrintAndLogEx(NORMAL, " e : elite computations applied to key"); - PrintAndLogEx(NORMAL, " r : raw, no computations applied to key (raw)"); -// PrintAndLogEx(NORMAL, " n : replay of NR/MAC"); - PrintAndLogEx(NORMAL, " v : verbose output"); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_("\thf iclass wrbl b 0A d AAAAAAAAAAAAAAAA k 001122334455667B")); - PrintAndLogEx(NORMAL, _YELLOW_("\thf iclass wrbl b 1B d AAAAAAAAAAAAAAAA k 001122334455667B c")); - PrintAndLogEx(NORMAL, _YELLOW_("\thf iclass wrbl b 1B d AAAAAAAAAAAAAAAA k 0")); - PrintAndLogEx(NORMAL, ""); - return PM3_SUCCESS; -} static int usage_hf_iclass_calc_newkey(void) { PrintAndLogEx(NORMAL, "Calculate new key for updating\n"); PrintAndLogEx(NORMAL, "Usage: hf iclass calc_newkey o n s [csn] e\n"); @@ -1675,103 +1655,91 @@ static int iclass_write_block(uint8_t blockno, uint8_t *bldata, uint8_t *KEY, bo } static int CmdHFiClass_WriteBlock(const char *Cmd) { - uint8_t blockno = 0; - uint8_t bldata[8] = {0, 0, 0, 0, 0, 0, 0, 0}; - uint8_t KEY[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - uint8_t keyNbr = 0; - uint8_t dataLen = 0; - char tempStr[50] = {0}; - bool got_blockno = false; - bool use_credit_key = false; - bool elite = false; - bool rawkey = false; - bool use_replay = false; - bool errors = false; - bool verbose = false; - bool use_secure_pagemode = false; - uint8_t cmdp = 0; - while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { - switch (tolower(param_getchar(Cmd, cmdp))) { - case 'h': - return usage_hf_iclass_writeblock(); - case 'b': - blockno = param_get8ex(Cmd, cmdp + 1, 07, 16); - got_blockno = true; - cmdp += 2; - break; - case 'c': - PrintAndLogEx(SUCCESS, "Using " _YELLOW_("CREDIT")); - use_credit_key = true; - cmdp++; - break; - case 'd': - if (param_gethex(Cmd, cmdp + 1, bldata, 16)) { - PrintAndLogEx(WARNING, "Data must include 16 HEX symbols\n"); - errors = true; - } - cmdp += 2; - break; - case 'e': - PrintAndLogEx(SUCCESS, "Using " _YELLOW_("elite algo")); - elite = true; - cmdp++; - break; - case 'k': - dataLen = param_getstr(Cmd, cmdp + 1, tempStr, sizeof(tempStr)); - if (dataLen == 16) { - errors = param_gethex(tempStr, 0, KEY, dataLen); - } else if (dataLen == 1) { - keyNbr = param_get8(Cmd, cmdp + 1); - if (keyNbr < ICLASS_KEYS_MAX) { - PrintAndLogEx(SUCCESS, "Using key[%d] %s", keyNbr, sprint_hex(iClass_Key_Table[keyNbr], 8)); - memcpy(KEY, iClass_Key_Table[keyNbr], 8); - } else { - PrintAndLogEx(WARNING, "\nERROR: Credit KeyNbr is invalid\n"); - errors = true; - } - } else { - PrintAndLogEx(WARNING, "\nERROR: Credit Key is incorrect length\n"); - errors = true; - } - use_secure_pagemode = true; - cmdp += 2; - break; - case 'r': - PrintAndLogEx(SUCCESS, "Using " _YELLOW_("raw mode")); - rawkey = true; - cmdp++; - break; - /* - case 'n': - PrintAndLogEx(SUCCESS, "Using " _YELLOW_("replay NR/MAC mode")); - use_replay = true; - cmdp++; - break; - */ - case 'v': - verbose = true; - cmdp++; - break; - default: - PrintAndLogEx(WARNING, "unknown parameter '%c'\n", param_getchar(Cmd, cmdp)); - errors = true; - break; + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf iclass wrbl", + "Write data to an iCLASS tag", + "hf iclass wrbl -b 10 -d AAAAAAAAAAAAAAAA -k 001122334455667B\n" + "hf iclass wrbl -b 27 -d AAAAAAAAAAAAAAAA -k 001122334455667B --credit\n" + "hf iclass wrbl -b 11 -d AAAAAAAAAAAAAAAA --ki 0"); + + void *argtable[] = { + arg_param_begin, + arg_str0("k", "key", "", "Access key as 16 hex symbols"), + arg_int0(NULL, "ki", "", "Key index to select key from memory 'hf iclass managekeys'"), + arg_int1("b", "block", "", "The block number to read as an integer"), + arg_str1("d", "data", "", "data to write as 16 hex symbols"), + arg_lit0(NULL, "credit", "key is assumed to be the credit key"), + arg_lit0(NULL, "elite", "elite computations applied to key"), + arg_lit0(NULL, "raw", "no computations applied to key (raw)"), + arg_lit0(NULL, "nr", "replay of NR/MAC"), + arg_lit0("v", "verbose", "verbose output"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + int key_len = 0; + uint8_t key[8] = {0}; + + CLIGetHexWithReturn(ctx, 1, key, &key_len); + + int key_nr = arg_get_int_def(ctx, 2, -1); + + if (key_len > 0 && key_nr >= 0) { + PrintAndLogEx(ERR, "Please specify key or index, not both"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + + bool auth = false; + + if (key_len > 0) { + auth = true; + if (key_len != 8) { + PrintAndLogEx(ERR, "Key is incorrect length"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + } else if (key_nr >= 0) { + if (key_nr < ICLASS_KEYS_MAX) { + auth = true; + memcpy(key, iClass_Key_Table[key_nr], 8); + PrintAndLogEx(SUCCESS, "Using key[%d] " _GREEN_("%s"), key_nr, sprint_hex(iClass_Key_Table[key_nr], 8)); + } else { + PrintAndLogEx(ERR, "Key number is invalid"); + CLIParserFree(ctx); + return PM3_EINVARG; } } - if (got_blockno == false) - errors = true; - if ((use_replay + rawkey + elite) > 1) { - PrintAndLogEx(FAILED, "Can not use a combo of 'e', 'r', 'n'"); - errors = true; + int blockno = arg_get_int_def(ctx, 3, 0); + + int data_len = 0; + uint8_t data[8] = {0}; + CLIGetHexWithReturn(ctx, 4, data, &data_len); + + if (data_len != 8) { + PrintAndLogEx(ERR, "Data must be 8 bytes (16 hex characters)"); + CLIParserFree(ctx); + return PM3_EINVARG; } - if (errors || cmdp < 4) return usage_hf_iclass_writeblock(); + bool use_credit_key = arg_get_lit(ctx, 5); + bool elite = arg_get_lit(ctx, 6); + bool rawkey = arg_get_lit(ctx, 7); + bool use_replay = arg_get_lit(ctx, 8); + bool verbose = arg_get_lit(ctx, 9); - int isok = iclass_write_block(blockno, bldata, KEY, use_credit_key, elite, rawkey, use_replay, verbose, use_secure_pagemode); + CLIParserFree(ctx); + + if ((use_replay + rawkey + elite) > 1) { + PrintAndLogEx(ERR, "Can not use a combo of 'elite', 'raw', 'nr'"); + return PM3_EINVARG; + } + + int isok = iclass_write_block(blockno, data, key, use_credit_key, elite, rawkey, use_replay, verbose, auth); switch (isok) { case PM3_SUCCESS: - PrintAndLogEx(SUCCESS, "Wrote block %02X successful", blockno); + PrintAndLogEx(SUCCESS, "Wrote block %3d/0x%02X successful", blockno, blockno); break; case PM3_ETEAROFF: if (verbose) @@ -1990,7 +1958,7 @@ static int iclass_read_block(uint8_t *KEY, uint8_t blockno, uint8_t keyType, boo } PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(SUCCESS, " block %02X : " _GREEN_("%s"), blockno, sprint_hex(packet->data, sizeof(packet->data))); + PrintAndLogEx(SUCCESS, " block %3d/0x%02X : " _GREEN_("%s"), blockno, blockno, sprint_hex(packet->data, sizeof(packet->data))); PrintAndLogEx(NORMAL, ""); if (out) @@ -2053,10 +2021,6 @@ static int CmdHFiClass_ReadBlock(const char *Cmd) { CLIParserFree(ctx); return PM3_EINVARG; } - } else { - PrintAndLogEx(ERR, "Please specify a key or key index"); - CLIParserFree(ctx); - return PM3_EINVARG; } int blockno = arg_get_int_def(ctx, 3, 0); diff --git a/doc/cheatsheet.md b/doc/cheatsheet.md index 3e092a173..409f7e483 100644 --- a/doc/cheatsheet.md +++ b/doc/cheatsheet.md @@ -93,11 +93,16 @@ Write to iCLASS Block ``` Options --- -b : The block number as 2 hex symbols -d : Set the Data to write as 16 hex symbols -k : Access Key as 16 hex symbols or 1 hex to select key from memory +-k, --key Access key as 16 hex symbols +-b, --block The block number to read as an integer +-d, --data data to write as 16 hex symbols + --ki Key index to select key from memory 'hf iclass managekeys' + --credit key is assumed to be the credit key + --elite elite computations applied to key + --raw no computations applied to key (raw) + --nr replay of NR/MAC -pm3 --> hf iclass wrbl b 07 d 6ce099fe7e614fd0 k 0 +pm3 --> hf iclass wrbl -b 7 -d 6ce099fe7e614fd0 --ki 0 ``` Print keystore @@ -155,7 +160,7 @@ pm3 --> hf iclass eload -f hf-iclass-db883702f8ff12e0.bin Clone iCLASS Legacy Sequence ``` pm3 --> hf iclass rdbl -b 7 --ki 0 -pm3 --> hf iclass wrbl b 7 d 6ce099fe7e614fd0 k 0 +pm3 --> hf iclass wrbl -b 7 -d 6ce099fe7e614fd0 --ki 0 ``` Simulate iCLASS From 62196c72289f187240a3ac0c1b2cce54a4231257 Mon Sep 17 00:00:00 2001 From: tcprst Date: Thu, 26 Nov 2020 00:34:30 -0500 Subject: [PATCH 37/52] standardize argument types - hex,dec,etc --- client/src/cmdhficlass.c | 44 ++++++++++++++++++++-------------------- doc/cheatsheet.md | 38 +++++++++++++++++----------------- 2 files changed, 41 insertions(+), 41 deletions(-) diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index 6bb215344..49784c4f5 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -947,8 +947,8 @@ static int CmdHFiClassDecrypt(const char *Cmd) { void *argtable[] = { arg_param_begin, arg_str0("f", "file", "", "filename of dumpfile"), - arg_str0("d", "data", "", "3DES encrypted data"), - arg_str0("k", "key", "", "3DES transport key"), + arg_str0("d", "data", "", "3DES encrypted data"), + arg_str0("k", "key", "", "3DES transport key"), arg_lit0("v", "verbose", "verbose output"), arg_param_end }; @@ -1183,8 +1183,8 @@ static int CmdHFiClassEncryptBlk(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_str1("d", "data", "", "data to encrypt"), - arg_str0("k", "key", "", "3DES transport key"), + arg_str1("d", "data", "", "data to encrypt"), + arg_str0("k", "key", "", "3DES transport key"), arg_lit0("v", "verbose", "verbose output"), arg_param_end }; @@ -1292,10 +1292,10 @@ static int CmdHFiClassDump(const char *Cmd) { void *argtable[] = { arg_param_begin, arg_str0("f", "file", "", "filename to save dump to"), - arg_str0("k", "key", "", "debit key as 16 hex symbols OR NR/MAC for replay"), - arg_int0(NULL, "ki", "", "debit key index to select key from memory 'hf iclass managekeys'"), - arg_str0(NULL, "credit", "", "credit key as 16 hex symbols"), - arg_int0(NULL, "ci", "", "credit key index to select key from memory 'hf iclass managekeys'"), + arg_str0("k", "key", "", "debit key as 16 hex symbols OR NR/MAC for replay"), + arg_int0(NULL, "ki", "", "debit key index to select key from memory 'hf iclass managekeys'"), + arg_str0(NULL, "credit", "", "credit key as 16 hex symbols"), + arg_int0(NULL, "ci", "", "credit key index to select key from memory 'hf iclass managekeys'"), arg_lit0(NULL, "elite", "elite computations applied to key"), arg_lit0(NULL, "raw", "raw, the key is interpreted as raw block 3/4"), arg_lit0(NULL, "nr", "replay of NR/MAC"), @@ -1664,10 +1664,10 @@ static int CmdHFiClass_WriteBlock(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_str0("k", "key", "", "Access key as 16 hex symbols"), - arg_int0(NULL, "ki", "", "Key index to select key from memory 'hf iclass managekeys'"), - arg_int1("b", "block", "", "The block number to read as an integer"), - arg_str1("d", "data", "", "data to write as 16 hex symbols"), + arg_str0("k", "key", "", "Access key as 16 hex symbols"), + arg_int0(NULL, "ki", "", "Key index to select key from memory 'hf iclass managekeys'"), + arg_int1("b", "block", "", "The block number to read as an integer"), + arg_str1("d", "data", "", "data to write as 16 hex symbols"), arg_lit0(NULL, "credit", "key is assumed to be the credit key"), arg_lit0(NULL, "elite", "elite computations applied to key"), arg_lit0(NULL, "raw", "no computations applied to key (raw)"), @@ -1763,10 +1763,10 @@ static int CmdHFiClassRestore(const char *Cmd) { void *argtable[] = { arg_param_begin, arg_str1("f", "file", "", "specify a filename to restore"), - arg_str0("k", "key", "", "Access key as 16 hex symbols"), - arg_int0(NULL, "ki", "", "Key index to select key from memory 'hf iclass managekeys'"), - arg_int1(NULL, "first", "", "The first block number to restore as an integer"), - arg_int1(NULL, "last", "", "The last block number to restore as an integer"), + arg_str0("k", "key", "", "Access key as 16 hex symbols"), + arg_int0(NULL, "ki", "", "Key index to select key from memory 'hf iclass managekeys'"), + arg_int1(NULL, "first", "", "The first block number to restore as an integer"), + arg_int1(NULL, "last", "", "The last block number to restore as an integer"), arg_lit0(NULL, "credit", "key is assumed to be the credit key"), arg_lit0(NULL, "elite", "elite computations applied to key"), arg_lit0(NULL, "raw", "no computations applied to key (raw)"), @@ -1977,9 +1977,9 @@ static int CmdHFiClass_ReadBlock(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_str0("k", "key", "", "Access key as 16 hex symbols"), - arg_int0(NULL, "ki", "", "Key index to select key from memory 'hf iclass managekeys'"), - arg_int1("b", "block", "", "The block number to read as an integer"), + arg_str0("k", "key", "", "Access key as 16 hex symbols"), + arg_int0(NULL, "ki", "", "Key index to select key from memory 'hf iclass managekeys'"), + arg_int1("b", "block", "", "The block number to read as an integer"), arg_lit0(NULL, "credit", "key is assumed to be the credit key"), arg_lit0(NULL, "elite", "elite computations applied to key"), arg_lit0(NULL, "raw", "no computations applied to key (raw)"), @@ -2298,8 +2298,8 @@ static int CmdHFiClassView(const char *Cmd) { void *argtable[] = { arg_param_begin, arg_str1("f", "file", "", "filename of dump"), - arg_int0(NULL, "first", "", "Begin printing from this block (default block6)"), - arg_int0(NULL, "last", "", "End printing at this block (default 0, ALL)"), + arg_int0(NULL, "first", "", "Begin printing from this block (default block6)"), + arg_int0(NULL, "last", "", "End printing at this block (default 0, ALL)"), arg_lit0("v", "verbose", "verbose output"), arg_param_end }; @@ -3224,7 +3224,7 @@ static int CmdHFiClassPermuteKey(const char *Cmd) { void *argtable[] = { arg_param_begin, arg_lit0("r", "reverse", "reverse permuted key"), - arg_str1(NULL, "key", "", "input key"), + arg_str1(NULL, "key", "", "input key"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); diff --git a/doc/cheatsheet.md b/doc/cheatsheet.md index 409f7e483..e725d8d25 100644 --- a/doc/cheatsheet.md +++ b/doc/cheatsheet.md @@ -63,10 +63,10 @@ Dump iCLASS card contents Options --- -f, --file filename to save dump to --k, --key debit key as 16 hex symbols OR NR/MAC for replay - --ki debit key index to select key from memory 'hf iclass managekeys' - --credit credit key as 16 hex symbols - --ci credit key index to select key from memory 'hf iclass managekeys' +-k, --key debit key as 16 hex symbols OR NR/MAC for replay + --ki debit key index to select key from memory 'hf iclass managekeys' + --credit credit key as 16 hex symbols + --ci credit key index to select key from memory 'hf iclass managekeys' --elite elite computations applied to key --raw raw, the key is interpreted as raw block 3/4 --nr replay of NR/MAC @@ -78,9 +78,9 @@ Read iCLASS Block ``` Options --- --k, --key Access key as 16 hex symbols --b, --block The block number to read as an integer - --ki Key index to select key from memory 'hf iclass managekeys' +-k, --key Access key as 16 hex symbols +-b, --block The block number to read as an integer + --ki Key index to select key from memory 'hf iclass managekeys' --credit key is assumed to be the credit key --elite elite computations applied to key --raw no computations applied to key (raw) @@ -93,14 +93,14 @@ Write to iCLASS Block ``` Options --- --k, --key Access key as 16 hex symbols --b, --block The block number to read as an integer --d, --data data to write as 16 hex symbols - --ki Key index to select key from memory 'hf iclass managekeys' - --credit key is assumed to be the credit key - --elite elite computations applied to key - --raw no computations applied to key (raw) - --nr replay of NR/MAC +-k, --key Access key as 16 hex symbols +-b, --block The block number to read as an integer +-d, --data data to write as 16 hex symbols + --ki Key index to select key from memory 'hf iclass managekeys' + --credit key is assumed to be the credit key + --elite elite computations applied to key + --raw no computations applied to key (raw) + --nr replay of NR/MAC pm3 --> hf iclass wrbl -b 7 -d 6ce099fe7e614fd0 --ki 0 ``` @@ -128,8 +128,8 @@ Encrypt iCLASS Block ``` Options --- --d, --data data to encrypt --k, --key 3DES transport key +-d, --data data to encrypt +-k, --key 3DES transport key -v, --verbose verbose output pm3 --> hf iclass encrypt -d 0000000f2aa3dba8 @@ -140,8 +140,8 @@ Decrypt iCLASS Block / file Options --- -f, --file filename of dumpfile --d, --data 3DES encrypted data --k, --key 3DES transport key +-d, --data 3DES encrypted data +-k, --key 3DES transport key -v, --verbose verbose output pm3 --> hf iclass decrypt -d 2AD4C8211F996871 From db083034d612831024bc26c76c6f24a3afd37955 Mon Sep 17 00:00:00 2001 From: tcprst Date: Thu, 26 Nov 2020 00:44:26 -0500 Subject: [PATCH 38/52] make style --- armsrc/lfops.c | 2 +- client/src/cmdlfpyramid.c | 6 +++--- client/src/cmdlfsecurakey.c | 2 +- client/src/cmdlfviking.c | 4 ++-- client/src/emv/emvcore.c | 2 +- doc/commands.md | 19 +++++++++---------- 6 files changed, 17 insertions(+), 18 deletions(-) diff --git a/armsrc/lfops.c b/armsrc/lfops.c index ac34c2a69..0d578295f 100644 --- a/armsrc/lfops.c +++ b/armsrc/lfops.c @@ -2258,7 +2258,7 @@ void CopyVikingtoT55xx(uint8_t *blocks, bool q5, bool em) { if (q5) { data[0] = T5555_SET_BITRATE(32) | T5555_MODULATION_MANCHESTER | 2 << T5555_MAXBLOCK_SHIFT; } else if (em) { - data[0] = (EM4x05_SET_BITRATE(32) | EM4x05_MODULATION_MANCHESTER | EM4x05_SET_NUM_BLOCKS(2) ); + data[0] = (EM4x05_SET_BITRATE(32) | EM4x05_MODULATION_MANCHESTER | EM4x05_SET_NUM_BLOCKS(2)); } data[1] = bytes_to_num(blocks, 4); diff --git a/client/src/cmdlfpyramid.c b/client/src/cmdlfpyramid.c index 1973bad49..452f3f61e 100644 --- a/client/src/cmdlfpyramid.c +++ b/client/src/cmdlfpyramid.c @@ -220,10 +220,10 @@ static int CmdPyramidClone(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_u64_1(NULL,"fc", "", "8-bit value facility code"), + arg_u64_1(NULL, "fc", "", "8-bit value facility code"), arg_u64_1(NULL, "cn", "", "16-bit value card number"), arg_lit0(NULL, "q5", "optional - specify writing to Q5/T5555 tag"), - arg_lit0(NULL, "em", "optional - specify writing to EM4305/4469 tag"), + arg_lit0(NULL, "em", "optional - specify writing to EM4305/4469 tag"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -301,7 +301,7 @@ static int CmdPyramidSim(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_u64_1(NULL,"fc", "", "8-bit value facility code"), + arg_u64_1(NULL, "fc", "", "8-bit value facility code"), arg_u64_1(NULL, "cn", "", "16-bit value card number"), arg_param_end }; diff --git a/client/src/cmdlfsecurakey.c b/client/src/cmdlfsecurakey.c index e54d0ff9c..c489427e2 100644 --- a/client/src/cmdlfsecurakey.c +++ b/client/src/cmdlfsecurakey.c @@ -105,7 +105,7 @@ int demodSecurakey(bool verbose) { if (bitLen <= 32) PrintAndLogEx(SUCCESS, "Wiegand: " _GREEN_("%08X") " parity (%s)", (lWiegand << (bitLen / 2)) | rWiegand, parity ? _GREEN_("ok") : _RED_("fail")); - if ( verbose ) { + if (verbose) { PrintAndLogEx(INFO, "\nHow the FC translates to printed FC is unknown"); PrintAndLogEx(INFO, "How the checksum is calculated is unknown"); PrintAndLogEx(INFO, "Help the community identify this format further\nby sharing your tag on the pm3 forum or discord"); diff --git a/client/src/cmdlfviking.c b/client/src/cmdlfviking.c index 2d60c4893..805eb875c 100644 --- a/client/src/cmdlfviking.c +++ b/client/src/cmdlfviking.c @@ -98,7 +98,7 @@ static int CmdVikingClone(const char *Cmd) { arg_param_begin, arg_strx0(NULL, "cn", "", "8 digit hex viking card number"), arg_lit0(NULL, "q5", "optional - specify writing to Q5/T5555 tag"), - arg_lit0(NULL, "em", "optional - specify writing to EM4305/4469 tag"), + arg_lit0(NULL, "em", "optional - specify writing to EM4305/4469 tag"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -134,7 +134,7 @@ static int CmdVikingClone(const char *Cmd) { num_to_bytes(rawID, 8, &payload.blocks[0]); char cardtype[16] = {"T55x7"}; - if (q5) + if (q5) snprintf(cardtype, sizeof(cardtype), "Q5/T5555"); else if (em) snprintf(cardtype, sizeof(cardtype), "EM4305/4469"); diff --git a/client/src/emv/emvcore.c b/client/src/emv/emvcore.c index 0bcd547b1..7982ff711 100644 --- a/client/src/emv/emvcore.c +++ b/client/src/emv/emvcore.c @@ -807,7 +807,7 @@ int trDDA(EMVCommandChannel channel, bool decodeTLV, struct tlvdb *tlv) { tlvdb_free(atc_db); return 9; } - + } else { struct tlvdb *dac_db = emv_pki_recover_dac(issuer_pk, tlv, sda_tlv); if (dac_db) { diff --git a/doc/commands.md b/doc/commands.md index e52c2361d..770d9f4c6 100644 --- a/doc/commands.md +++ b/doc/commands.md @@ -11,7 +11,7 @@ Check column "offline" for their availability. |command |offline |description |------- |------- |----------- |`auto `|N |`Automated detection process for unknown tags` -|`clear `|Y |`clear screen` +|`clear `|Y |`Clear screen` |`help `|Y |`This help. Use ' help' for details of a particular command.` |`hints `|Y |`Turn hints on / off` |`msleep `|Y |`Add a pause in milliseconds` @@ -256,7 +256,6 @@ Check column "offline" for their availability. |`hf iclass restore `|N |`[options..] Restore a dump file onto a Picopass / iCLASS tag` |`hf iclass sniff `|N |` Eavesdrop Picopass / iCLASS communication` |`hf iclass wrbl `|N |`[options..] Write Picopass / iCLASS block` -|`hf iclass autopwn `|N |`[options..] Automatic key recovery tool for iCLASS` |`hf iclass chk `|N |`[options..] Check keys` |`hf iclass loclass `|Y |`[options..] Use loclass to perform bruteforce reader attack` |`hf iclass lookup `|Y |`[options..] Uses authentication trace to check for key in dictionary file` @@ -850,7 +849,7 @@ Check column "offline" for their availability. |------- |------- |----------- |`lf pyramid help `|Y |`this help` |`lf pyramid demod `|Y |`demodulate a Pyramid FSK tag from the GraphBuffer` -|`lf pyramid read `|N |`attempt to read and extract tag data` +|`lf pyramid reader `|N |`attempt to read and extract tag data` |`lf pyramid clone `|N |`clone pyramid tag to T55x7 or Q5/T5555` |`lf pyramid sim `|N |`simulate pyramid tag` @@ -863,7 +862,7 @@ Check column "offline" for their availability. |------- |------- |----------- |`lf securakey help `|Y |`This help` |`lf securakey demod `|Y |`Demodulate an Securakey tag from the GraphBuffer` -|`lf securakey read `|N |`Attempt to read and extract tag data from the antenna` +|`lf securakey reader `|N |`Attempt to read and extract tag data from the antenna` |`lf securakey clone `|N |`clone Securakey tag to T55x7` |`lf securakey sim `|N |`simulate Securakey tag` @@ -876,7 +875,7 @@ Check column "offline" for their availability. |------- |------- |----------- |`lf ti help `|Y |`This help` |`lf ti demod `|Y |`Demodulate raw bits for TI-type LF tag from the GraphBuffer` -|`lf ti read `|N |`Read and decode a TI 134 kHz tag` +|`lf ti reader `|N |`Read and decode a TI 134 kHz tag` |`lf ti write `|N |`Write new data to a r/w TI 134 kHz tag` @@ -918,7 +917,7 @@ Check column "offline" for their availability. |------- |------- |----------- |`lf viking help `|Y |`This help` |`lf viking demod `|Y |`Demodulate a Viking tag from the GraphBuffer` -|`lf viking read `|N |`Attempt to read and Extract tag data from the antenna` +|`lf viking reader `|N |`Attempt to read and Extract tag data from the antenna` |`lf viking clone `|N |`clone Viking tag to T55x7 or Q5/T5555` |`lf viking sim `|N |`simulate Viking tag` @@ -931,14 +930,14 @@ Check column "offline" for their availability. |------- |------- |----------- |`lf visa2000 help `|Y |`This help` |`lf visa2000 demod `|Y |`demodulate an VISA2000 tag from the GraphBuffer` -|`lf visa2000 read `|N |`attempt to read and extract tag data from the antenna` +|`lf visa2000 reader `|N |`attempt to read and extract tag data from the antenna` |`lf visa2000 clone `|N |`clone Visa2000 tag to T55x7 or Q5/T5555` |`lf visa2000 sim `|N |`simulate Visa2000 tag` ### mem - { Flash Memory manipulation... } + { Flash memory manipulation... } |command |offline |description |------- |------- |----------- @@ -953,7 +952,7 @@ Check column "offline" for their availability. ### reveng - { CRC calculations from RevEng software } + { CRC calculations from RevEng software... } [=] reveng: no mode switch specified. Use reveng -h for help. @@ -975,7 +974,7 @@ Check column "offline" for their availability. ### script - { Scripting commands } + { Scripting commands... } |command |offline |description |------- |------- |----------- From 2e1c906d9e0eae264a834c987ebfdecaea85c954 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Thu, 26 Nov 2020 07:07:33 +0100 Subject: [PATCH 39/52] text --- client/src/cmdlfviking.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/cmdlfviking.c b/client/src/cmdlfviking.c index 805eb875c..bdf9868fb 100644 --- a/client/src/cmdlfviking.c +++ b/client/src/cmdlfviking.c @@ -90,7 +90,7 @@ static int CmdVikingClone(const char *Cmd) { CLIParserInit(&ctx, "lf viking clone", "clone a Viking AM tag to a T55x7, Q5/T5555 or EM4305/4469 tag.", "lf viking clone --cn 01A337\n" - "lf viking clone --cn 01A337 --q5 -> encode for Q5/T5555 tag" + "lf viking clone --cn 01A337 --q5 -> encode for Q5/T5555 tag\n" "lf viking clone --cn 112233 --em -> encode for EM4305/4469" ); From c59d6cdc71d8230f26bad51f356a7d8c936710d4 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Thu, 26 Nov 2020 19:42:24 +0100 Subject: [PATCH 40/52] lf presco - now uses cliparser, continuous mode and clone to EM (untested) --- client/src/cmdlfpresco.c | 355 +++++++++++++++++++++++---------------- client/src/cmdlfpresco.h | 5 - 2 files changed, 209 insertions(+), 151 deletions(-) diff --git a/client/src/cmdlfpresco.c b/client/src/cmdlfpresco.c index d8bca9c19..3857cdd83 100644 --- a/client/src/cmdlfpresco.c +++ b/client/src/cmdlfpresco.c @@ -8,51 +8,64 @@ //----------------------------------------------------------------------------- #include "cmdlfpresco.h" - #include #include #include #include - -#include "commonutil.h" // ARRAYLEN +#include "commonutil.h" // ARRAYLEN #include "cmdparser.h" // command_t #include "comms.h" #include "ui.h" #include "cmddata.h" #include "cmdlf.h" -#include "protocols.h" // for T55xx config register definitions -#include "lfdemod.h" // parityTest -#include "cmdlft55xx.h" // verifywrite +#include "protocols.h" // for T55xx config register definitions +#include "lfdemod.h" // parityTest +#include "cmdlft55xx.h" // verifywrite +#include "cmdlfem4x05.h" // +#include "cliparser.h" static int CmdHelp(const char *Cmd); -static int usage_lf_presco_clone(void) { - PrintAndLogEx(NORMAL, "clone a Presco tag to a T55x7 or Q5/T5555 tag."); - PrintAndLogEx(NORMAL, "Usage: lf presco clone [h] d c "); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h : this help"); - PrintAndLogEx(NORMAL, " d : 9 digit presco card ID"); - PrintAndLogEx(NORMAL, " c : 8 digit hex card number"); - PrintAndLogEx(NORMAL, " : specify writing to Q5/T5555 tag"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" lf presco clone d 123456789")); +// find presco preamble 0x10D in already demoded data +static int detectPresco(uint8_t *dest, size_t *size) { + if (*size < 128 * 2) return -1; //make sure buffer has data + size_t startIdx = 0; + uint8_t preamble[] = {0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + if (!preambleSearch(dest, preamble, sizeof(preamble), size, &startIdx)) + return -2; //preamble not found + if (*size != 128) return -3; //wrong demoded size + //return start position + return (int)startIdx; +} + +// convert base 12 ID to sitecode & usercode & 8 bit other unknown code +static int getWiegandFromPrintedPresco(void *arr, uint32_t *fullcode) { + char *s = (char*)arr; + uint8_t val = 0; + for (int i = 0; i < strlen(s); ++i) { + // Get value from number string. + if (s[i] == '*') + val = 10; + if (s[i] == '#') + val = 11; + if (s[i] >= 0x30 && s[i] <= 0x39) + val = s[i] - 0x30; + + *fullcode += val; + + // last digit is only added, not multipled. + if (i < strlen(s) - 1) + *fullcode *= 12; + } return PM3_SUCCESS; } -static int usage_lf_presco_sim(void) { - PrintAndLogEx(NORMAL, "Enables simulation of presco card with specified card number."); - PrintAndLogEx(NORMAL, "Simulation runs until the button is pressed or another USB command is issued."); - PrintAndLogEx(NORMAL, "Per presco format, the card number is 9 digit number and can contain *# chars. Larger values are truncated."); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Usage: lf presco sim [h] d or c "); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h : this help"); - PrintAndLogEx(NORMAL, " d : 9 digit presco card number"); - PrintAndLogEx(NORMAL, " c : 8 digit hex card number"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" lf presco sim d 123456789")); +// calc not certain - intended to get bitstream for programming / sim +static int getPrescoBits(uint32_t fullcode, uint8_t *prescoBits) { + num_to_bytebits(0x10D00000, 32, prescoBits); + num_to_bytebits(0x00000000, 32, prescoBits + 32); + num_to_bytebits(0x00000000, 32, prescoBits + 64); + num_to_bytebits(fullcode, 32, prescoBits + 96); return PM3_SUCCESS; } @@ -85,15 +98,16 @@ int demodPresco(bool verbose) { uint32_t raw2 = bytebits_to_byte(DemodBuffer + 32, 32); uint32_t raw3 = bytebits_to_byte(DemodBuffer + 64, 32); uint32_t raw4 = bytebits_to_byte(DemodBuffer + 96, 32); - uint32_t cardid = raw4; - PrintAndLogEx(SUCCESS, "Presco - Card: " _GREEN_("%08X") ", Raw: %08X%08X%08X%08X", cardid, raw1, raw2, raw3, raw4); + uint32_t fullcode = raw4; + uint32_t usercode = fullcode & 0x0000FFFF; + uint32_t sitecode = (fullcode >> 24) & 0x000000FF; - uint32_t sitecode = 0, usercode = 0, fullcode = 0; - bool Q5 = false; - char cmd[12] = {0}; - sprintf(cmd, "H %08X", cardid); - getWiegandFromPresco(cmd, &sitecode, &usercode, &fullcode, &Q5); - PrintAndLogEx(SUCCESS, "SiteCode: " _GREEN_("%u") " UserCode: " _GREEN_("%u") " FullCode: " _GREEN_("%08X"), sitecode, usercode, fullcode); + PrintAndLogEx(SUCCESS, "Presco Site code: " _GREEN_("%u") " User code: " _GREEN_("%u") " Full code: " _GREEN_("%08X") " Raw: " _YELLOW_("%08X%08X%08X%08X") + , sitecode + , usercode + , fullcode + , raw1, raw2, raw3, raw4 + ); return PM3_SUCCESS; } @@ -103,35 +117,108 @@ static int CmdPrescoDemod(const char *Cmd) { } //see ASKDemod for what args are accepted -static int CmdPrescoRead(const char *Cmd) { - // Presco Number: 123456789 --> Sitecode 30 | usercode 8665 - (void)Cmd; // Cmd is not used so far - lf_read(false, 12000); - return demodPresco(true); +static int CmdPrescoReader(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf presco reader", + "read a presco tag", + "lf presco reader -@ -> continuous reader mode" + ); + + void *argtable[] = { + arg_param_begin, + arg_lit0("@", NULL, "optional - continuous reader mode"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + bool cm = arg_get_lit(ctx, 1); + CLIParserFree(ctx); + + do { + lf_read(false, 12000); + demodPresco(!cm); + } while (cm && !kbd_enter_pressed()); + return PM3_SUCCESS; } // takes base 12 ID converts to hex // Or takes 8 digit hex ID static int CmdPrescoClone(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf presco clone", + "clone a presco tag to a T55x7, Q5/T5555 or EM4305/4469 tag.", + "lf presco clone -d 018363467\n" + "lf presco clone -d 018363467 --q5 -> encode for Q5/T5555 tag\n" + "lf presco clone -d 018363467 --em -> encode for EM4305/4469" + ); + + void *argtable[] = { + arg_param_begin, + arg_str0("c", NULL, "", "8 digit hex card number"), + arg_str0("d", NULL, "", "9 digit presco card ID"), + arg_lit0(NULL, "q5", "optional - specify writing to Q5/T5555 tag"), + arg_lit0(NULL, "em", "optional - specify writing to EM4305/4469 tag"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + int hex_len = 0; + uint8_t hex[4] = {0,0,0,0}; + CLIGetHexWithReturn(ctx, 1, hex, &hex_len); + + uint8_t idstr[11]; + int slen = 9; + memset(idstr, 0x00, sizeof(idstr)); + CLIGetStrWithReturn(ctx, 2, idstr, &slen); + + bool q5 = arg_get_lit(ctx, 3); + bool em = arg_get_lit(ctx, 4); + CLIParserFree(ctx); + + if (q5 && em) { + PrintAndLogEx(FAILED, "Can't specify both Q5 and EM4305 at the same time"); + return PM3_EINVARG; + } + + uint32_t fullcode = 0; + + if (hex_len) { + fullcode = bytes_to_num(hex, hex_len); + } else { + //param get string int param_getstr(const char *line, int paramnum, char * str) + if (slen < 2) { + PrintAndLogEx(ERR, "Must contain atleast 2 digits"); + return PM3_EINVARG; + } + + getWiegandFromPrintedPresco(idstr, &fullcode); + } + + uint32_t usercode = fullcode & 0x0000FFFF; //% 65566 + uint32_t sitecode = (fullcode >> 24) & 0x000000FF; // /= 16777216; - bool Q5 = false; - uint32_t sitecode = 0, usercode = 0, fullcode = 0; uint32_t blocks[5] = {T55x7_MODULATION_MANCHESTER | T55x7_BITRATE_RF_32 | 4 << T55x7_MAXBLOCK_SHIFT | T55x7_ST_TERMINATOR, 0, 0, 0, 0}; - // get wiegand from printed number. - if (getWiegandFromPresco(Cmd, &sitecode, &usercode, &fullcode, &Q5) == PM3_EINVARG) return usage_lf_presco_clone(); - - if (Q5) + char cardtype[16] = {"T55x7"}; + // Q5 + if (q5) { blocks[0] = T5555_FIXED | T5555_MODULATION_MANCHESTER | T5555_SET_BITRATE(32) | 4 << T5555_MAXBLOCK_SHIFT | T5555_ST_TERMINATOR; + snprintf(cardtype, sizeof(cardtype), "Q5/T5555"); + } + + // EM4305 + if (em) { + blocks[0] = EM4305_PRESCO_CONFIG_BLOCK; + snprintf(cardtype, sizeof(cardtype), "EM4305/4469"); + } if ((sitecode & 0xFF) != sitecode) { sitecode &= 0xFF; - PrintAndLogEx(INFO, "Facility-Code Truncated to 8-bits (Presco): %u", sitecode); + PrintAndLogEx(INFO, "Site code truncated to 8-bits (Presco): %u", sitecode); } if ((usercode & 0xFFFF) != usercode) { usercode &= 0xFFFF; - PrintAndLogEx(INFO, "Card Number Truncated to 16-bits (Presco): %u", usercode); + PrintAndLogEx(INFO, "User code truncated to 16-bits (Presco): %u", usercode); } blocks[1] = 0x10D00000; //preamble @@ -139,25 +226,84 @@ static int CmdPrescoClone(const char *Cmd) { blocks[3] = 0x00000000; blocks[4] = fullcode; - PrintAndLogEx(INFO, "Preparing to clone Presco to " _YELLOW_("%s") " with SiteCode: %u, UserCode: %u, FullCode: %08x", (Q5) ? "Q5/T5555" : "T55x7", sitecode, usercode, fullcode); + PrintAndLogEx(INFO, "Preparing to clone Presco to " _GREEN_("%s") " with Site code: " _GREEN_("%u") " User code: " _GREEN_("%u") " Full code: " _GREEN_("%08x") + , cardtype + , sitecode + , usercode + , fullcode + ); print_blocks(blocks, ARRAYLEN(blocks)); - int res = clone_t55xx_tag(blocks, ARRAYLEN(blocks)); + int res; + if (em) { + res = em4x05_clone_tag(blocks, ARRAYLEN(blocks), 0, false); + } else { + res = clone_t55xx_tag(blocks, ARRAYLEN(blocks)); + } PrintAndLogEx(SUCCESS, "Done"); - PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf presco read`") " to verify"); + PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf presco reader`") " to verify"); return res; } // takes base 12 ID converts to hex // Or takes 8 digit hex ID static int CmdPrescoSim(const char *Cmd) { - uint32_t sitecode = 0, usercode = 0, fullcode = 0; - bool Q5 = false; - // get wiegand from printed number. - if (getWiegandFromPresco(Cmd, &sitecode, &usercode, &fullcode, &Q5) == PM3_EINVARG) - return usage_lf_presco_sim(); + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf presco sim", + "Enables simulation of presco card with specified card number.\n" + "Simulation runs until the button is pressed or another USB command is issued.\n" + "Per presco format, the card number is 9 digit number and can contain *# chars. Larger values are truncated.", + "lf presco sim -d 018363467" + ); - PrintAndLogEx(SUCCESS, "Simulating Presco - SiteCode: %u, UserCode: %u, FullCode: %08X", sitecode, usercode, fullcode); + void *argtable[] = { + arg_param_begin, + arg_str0("c", NULL, "", "8 digit hex card number"), + arg_str0("d", NULL, "", "9 digit presco card ID"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + int hex_len = 0; + uint8_t hex[4] = {0,0,0,0}; + CLIGetHexWithReturn(ctx, 1, hex, &hex_len); + + uint8_t idstr[11]; + int slen = 9; + memset(idstr, 0x00, sizeof(idstr)); + CLIGetStrWithReturn(ctx, 2, idstr, &slen); + CLIParserFree(ctx); + + uint32_t fullcode = 0; + + if (hex_len) { + fullcode = bytes_to_num(hex, hex_len); + } else { + if (slen < 2) { + PrintAndLogEx(ERR, "Must contain atleast 2 digits"); + return PM3_EINVARG; + } + getWiegandFromPrintedPresco(idstr, &fullcode); + } + + uint32_t usercode = fullcode & 0x0000FFFF; + uint32_t sitecode = (fullcode >> 24) & 0x000000FF; + + if ((sitecode & 0xFF) != sitecode) { + sitecode &= 0xFF; + PrintAndLogEx(INFO, "Site code truncated to 8-bits (Presco): %u", sitecode); + } + + if ((usercode & 0xFFFF) != usercode) { + usercode &= 0xFFFF; + PrintAndLogEx(INFO, "User code truncated to 16-bits (Presco): %u", usercode); + } + + PrintAndLogEx(SUCCESS, "Simulating Presco - Site Code: " _GREEN_("%u") " User Code: " _GREEN_("%u") " Full Code: " _GREEN_("%08X") + , sitecode + , usercode + , fullcode) + ; uint8_t bs[128]; getPrescoBits(fullcode, bs); @@ -185,9 +331,9 @@ static int CmdPrescoSim(const char *Cmd) { static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help"}, {"demod", CmdPrescoDemod, AlwaysAvailable, "demodulate Presco tag from the GraphBuffer"}, - {"read", CmdPrescoRead, IfPm3Lf, "Attempt to read and Extract tag data"}, - {"clone", CmdPrescoClone, IfPm3Lf, "clone presco tag to T55x7 or Q5/T5555"}, - {"sim", CmdPrescoSim, IfPm3Lf, "simulate presco tag"}, + {"reader", CmdPrescoReader, IfPm3Lf, "Attempt to read and Extract tag data"}, + {"clone", CmdPrescoClone, IfPm3Lf, "clone presco tag to T55x7 or Q5/T5555"}, + {"sim", CmdPrescoSim, IfPm3Lf, "simulate presco tag"}, {NULL, NULL, NULL, NULL} }; @@ -201,86 +347,3 @@ int CmdLFPresco(const char *Cmd) { clearCommandBuffer(); return CmdsParse(CommandTable, Cmd); } - -// find presco preamble 0x10D in already demoded data -int detectPresco(uint8_t *dest, size_t *size) { - if (*size < 128 * 2) return -1; //make sure buffer has data - size_t startIdx = 0; - uint8_t preamble[] = {0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - if (!preambleSearch(dest, preamble, sizeof(preamble), size, &startIdx)) - return -2; //preamble not found - if (*size != 128) return -3; //wrong demoded size - //return start position - return (int)startIdx; -} - -// convert base 12 ID to sitecode & usercode & 8 bit other unknown code -int getWiegandFromPresco(const char *Cmd, uint32_t *sitecode, uint32_t *usercode, uint32_t *fullcode, bool *Q5) { - - bool hex = false, errors = false; - uint8_t cmdp = 0; - char id[11]; - int stringlen = 0; - memset(id, 0x00, sizeof(id)); - - while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { - switch (tolower(param_getchar(Cmd, cmdp))) { - case 'h': - return PM3_EINVARG; - case 'c': - hex = true; - //get hex - *fullcode = param_get32ex(Cmd, cmdp + 1, 0, 16); - cmdp += 2; - break; - case 'd': - //param get string int param_getstr(const char *line, int paramnum, char * str) - stringlen = param_getstr(Cmd, cmdp + 1, id, sizeof(id)); - if (stringlen < 2) return PM3_EINVARG; - cmdp += 2; - break; - case 'q': - *Q5 = true; - cmdp++; - break; - default: - PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); - errors = 1; - break; - } - } - //Validations - if (errors || cmdp == 0) return PM3_EINVARG; - - if (!hex) { - uint8_t val = 0; - for (int index = 0; index < strlen(id); ++index) { - // Get value from number string. - if (id[index] == '*') - val = 10; - if (id[index] == '#') - val = 11; - if (id[index] >= 0x30 && id[index] <= 0x39) - val = id[index] - 0x30; - - *fullcode += val; - - // last digit is only added, not multipled. - if (index < strlen(id) - 1) - *fullcode *= 12; - } - } - - *usercode = *fullcode & 0x0000FFFF; //% 65566 - *sitecode = (*fullcode >> 24) & 0x000000FF; // /= 16777216; - return PM3_SUCCESS; -} - -// calc not certain - intended to get bitstream for programming / sim -int getPrescoBits(uint32_t fullcode, uint8_t *prescoBits) { - num_to_bytebits(0x10D00000, 32, prescoBits); - num_to_bytebits(0x00000000, 32, prescoBits + 32); - num_to_bytebits(0x00000000, 32, prescoBits + 64); - num_to_bytebits(fullcode, 32, prescoBits + 96); - return PM3_SUCCESS; -} diff --git a/client/src/cmdlfpresco.h b/client/src/cmdlfpresco.h index d90d6c573..961830ab2 100644 --- a/client/src/cmdlfpresco.h +++ b/client/src/cmdlfpresco.h @@ -12,11 +12,6 @@ #include "common.h" int CmdLFPresco(const char *Cmd); - int demodPresco(bool verbose); -int detectPresco(uint8_t *dest, size_t *size); -int getPrescoBits(uint32_t fullcode, uint8_t *prescoBits); -int getWiegandFromPresco(const char *Cmd, uint32_t *sitecode, uint32_t *usercode, uint32_t *fullcode, bool *Q5); - #endif From a1e94a100a2f7027ad3e6baba22a79293f81bc23 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Thu, 26 Nov 2020 20:07:39 +0100 Subject: [PATCH 41/52] text --- client/src/cmdlfviking.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/cmdlfviking.c b/client/src/cmdlfviking.c index bdf9868fb..0da4bcbd5 100644 --- a/client/src/cmdlfviking.c +++ b/client/src/cmdlfviking.c @@ -142,7 +142,7 @@ static int CmdVikingClone(const char *Cmd) { PrintAndLogEx(INFO, "Preparing to clone Viking tag on " _YELLOW_("%s") " - ID " _YELLOW_("%08X")" raw " _YELLOW_("%s") , cardtype , id - , sprint_hex(payload.blocks, sizeof(payload.blocks)) + , sprint_hex(payload.blocks, sizeof(payload.blocks)) ); clearCommandBuffer(); From 263215d242544948b6f79618677e6f1285526476 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Thu, 26 Nov 2020 20:08:04 +0100 Subject: [PATCH 42/52] text --- client/src/cmdlfsecurakey.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/client/src/cmdlfsecurakey.c b/client/src/cmdlfsecurakey.c index c489427e2..9e8158c82 100644 --- a/client/src/cmdlfsecurakey.c +++ b/client/src/cmdlfsecurakey.c @@ -175,7 +175,7 @@ static int CmdSecurakeyClone(const char *Cmd) { } if (raw_len != 12) { - PrintAndLogEx(ERR, "Data must be 12 bytes (24 HEX characters)"); + PrintAndLogEx(ERR, "Data must be 12 bytes (24 HEX characters) %d", raw_len); return PM3_EINVARG; } @@ -233,13 +233,12 @@ static int CmdSecurakeySim(const char *Cmd) { // skip first block, 3*4 = 12 bytes left uint8_t raw[12] = {0}; CLIGetHexWithReturn(ctx, 1, raw, &raw_len); + CLIParserFree(ctx); if (raw_len != 12) { PrintAndLogEx(ERR, "Data must be 12 bytes (24 HEX characters) %d", raw_len); - CLIParserFree(ctx); return PM3_EINVARG; } - CLIParserFree(ctx); PrintAndLogEx(SUCCESS, "Simulating SecuraKey - raw " _YELLOW_("%s"), sprint_hex_inrow(raw, sizeof(raw))); From 966ecd6193bd7eebc73628906eb96933bae13e58 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Thu, 26 Nov 2020 20:09:20 +0100 Subject: [PATCH 43/52] added lf paradox sim (experimental), lf paradox clone / reader - now uses cliparser, support continuous mode, EM (untested) --- client/src/cmdlfparadox.c | 238 ++++++++++++++++++++++---------------- 1 file changed, 136 insertions(+), 102 deletions(-) diff --git a/client/src/cmdlfparadox.c b/client/src/cmdlfparadox.c index 1b0aae510..08b67de1c 100644 --- a/client/src/cmdlfparadox.c +++ b/client/src/cmdlfparadox.c @@ -1,4 +1,5 @@ //----------------------------------------------------------------------------- +// Marshmellow // // This code is licensed to you under the terms of the GNU GPL, version 2 or, // at your option, any later version. See the LICENSE.txt file for the text of @@ -8,13 +9,11 @@ // FSK2a, rf/50, 96 bits (completely known) //----------------------------------------------------------------------------- #include "cmdlfparadox.h" - #include #include #include #include - -#include "commonutil.h" // ARRAYLEN +#include "commonutil.h" // ARRAYLEN #include "cmdparser.h" // command_t #include "comms.h" #include "ui.h" @@ -25,40 +24,11 @@ #include "protocols.h" // t55xx defines #include "cmdlft55xx.h" // clone.. #include "crc.h" // maxim +#include "cmdlfem4x05.h" // +#include "cliparser.h" static int CmdHelp(const char *Cmd); -static int usage_lf_paradox_clone(void) { - PrintAndLogEx(NORMAL, "clone a Paradox tag to a T55x7 tag."); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Usage: lf paradox clone [h] [b ]"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h : this help"); - PrintAndLogEx(NORMAL, " b : raw hex data. 12 bytes max"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" lf paradox clone b 0f55555695596a6a9999a59a")); - return PM3_SUCCESS; -} - -/* -static int usage_lf_paradox_sim(void) { - PrintAndLogEx(NORMAL, "Enables simulation of Paradox card with specified card number."); - PrintAndLogEx(NORMAL, "Simulation runs until the button is pressed or another USB command is issued."); - PrintAndLogEx(NORMAL, "The facility-code is 8-bit and the card number is 16-bit. Larger values are truncated."); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Usage: lf paradox sim [h] "); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h : this help"); - PrintAndLogEx(NORMAL, " : 8-bit value facility code"); - PrintAndLogEx(NORMAL, " : 16-bit value card number"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" lf paradox sim 123 11223")); - return PM3_SUCCESS; -} -*/ - const uint8_t paradox_lut[] = { 0xDB, 0xFC, 0x3F, 0xC5, 0x50, 0x14, 0x05, 0x47, 0x9F, 0xED, 0x7D, 0x59, 0x22, 0x84, 0x21, 0x4E, @@ -71,9 +41,9 @@ const uint8_t paradox_lut[] = { #define PARADOX_PREAMBLE_LEN 8 -//by marshmellow -//Paradox Prox demod - FSK2a RF/50 with preamble of 00001111 (then manchester encoded) -//print full Paradox Prox ID and some bit format details if found +// by marshmellow +// Paradox Prox demod - FSK2a RF/50 with preamble of 00001111 (then manchester encoded) +// print full Paradox Prox ID and some bit format details if found int demodParadox(bool verbose) { (void) verbose; // unused so far @@ -204,91 +174,141 @@ static int CmdParadoxDemod(const char *Cmd) { return demodParadox(true); } -//by marshmellow -//see ASKDemod for what args are accepted -static int CmdParadoxRead(const char *Cmd) { - (void)Cmd; // Cmd is not used so far - lf_read(false, 10000); - return demodParadox(true); +static int CmdParadoxReader(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf paradox reader", + "read a Paradox tag", + "lf Paradox reader -@ -> continuous reader mode" + ); + + void *argtable[] = { + arg_param_begin, + arg_lit0("@", NULL, "optional - continuous reader mode"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + bool cm = arg_get_lit(ctx, 1); + CLIParserFree(ctx); + + do { + lf_read(false, 10000); + demodParadox(!cm); + } while (cm && !kbd_enter_pressed()); + + return PM3_SUCCESS; } static int CmdParadoxClone(const char *Cmd) { - uint32_t blocks[4]; - bool errors = false; - uint8_t cmdp = 0; - int datalen = 0; + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf paradox clone", + "clone a paradox tag to a T55x7, Q5/T5555 or EM4305/4469 tag.", + "lf paradox clone --raw 0f55555695596a6a9999a59a\n" + "lf paradox clone -r 0f55555695596a6a9999a59a --q5 -> encode for Q5/T5555 tag\n" + "lf paradox clone -r 0f55555695596a6a9999a59a --em -> encode for EM4305/4469" + ); - while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { - switch (tolower(param_getchar(Cmd, cmdp))) { - case 'h': - return usage_lf_paradox_clone(); - case 'b': { - // skip first block, 3*4 =12 bytes left - uint8_t rawhex[12] = {0}; - int res = param_gethex_to_eol(Cmd, cmdp + 1, rawhex, sizeof(rawhex), &datalen); - if (res != 0) - errors = true; + void *argtable[] = { + arg_param_begin, + arg_str0("r", "raw", "", "raw hex data. 12 bytes max"), + arg_lit0(NULL, "q5", "optional - specify writing to Q5/T5555 tag"), + arg_lit0(NULL, "em", "optional - specify writing to EM4305/4469 tag"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); - for (uint8_t i = 1; i < ARRAYLEN(blocks); i++) { - blocks[i] = bytes_to_num(rawhex + ((i - 1) * 4), sizeof(uint32_t)); - } - cmdp += 2; - break; - } - default: - PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); - errors = true; - break; - } + int raw_len = 0; + // skip first block, 3*4 = 12 bytes left + uint8_t raw[12] = {0}; + CLIGetHexWithReturn(ctx, 1, raw, &raw_len); + + bool q5 = arg_get_lit(ctx, 2); + bool em = arg_get_lit(ctx, 3); + CLIParserFree(ctx); + + if (q5 && em) { + PrintAndLogEx(FAILED, "Can't specify both Q5 and EM4305 at the same time"); + return PM3_EINVARG; } - if (errors || cmdp == 0) return usage_lf_paradox_clone(); + if (raw_len != 12) { + PrintAndLogEx(ERR, "Data must be 12 bytes (24 HEX characters) %d", raw_len); + return PM3_EINVARG; + } - //Securakey - compat mode, ASK/Man, data rate 40, 3 data blocks + uint32_t blocks[4]; + for (uint8_t i = 1; i < ARRAYLEN(blocks); i++) { + blocks[i] = bytes_to_num(raw + ((i - 1) * 4), sizeof(uint32_t)); + } + + // Paradox - FSK2a, data rate 50, 3 data blocks blocks[0] = T55x7_MODULATION_FSK2a | T55x7_BITRATE_RF_50 | 3 << T55x7_MAXBLOCK_SHIFT; + char cardtype[16] = {"T55x7"}; + // Q5 + if (q5) { + blocks[0] = T5555_FIXED | T5555_INVERT_OUTPUT | T5555_MODULATION_FSK2 | T5555_SET_BITRATE(50) | T5555_ST_TERMINATOR | 3 << T5555_MAXBLOCK_SHIFT; + snprintf(cardtype, sizeof(cardtype), "Q5/T5555"); + } - PrintAndLogEx(INFO, "Preparing to clone Paradox to T55x7 with raw hex"); + // EM4305 + if (em) { + blocks[0] = EM4305_PARADOX_CONFIG_BLOCK; + snprintf(cardtype, sizeof(cardtype), "EM4305/4469"); + } + + PrintAndLogEx(INFO, "Preparing to clone Paradox to " _YELLOW_("%s") " with raw hex", cardtype); print_blocks(blocks, ARRAYLEN(blocks)); - int res = clone_t55xx_tag(blocks, ARRAYLEN(blocks)); + int res; + if (em) { + res = em4x05_clone_tag(blocks, ARRAYLEN(blocks), 0, false); + } else { + res = clone_t55xx_tag(blocks, ARRAYLEN(blocks)); + } PrintAndLogEx(SUCCESS, "Done"); PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf paradox read`") " to verify"); return res; } static int CmdParadoxSim(const char *Cmd) { - PrintAndLogEx(INFO, " To be implemented, feel free to contribute!"); - return PM3_SUCCESS; -} -/* - char cmdp = tolower(param_getchar(Cmd, 0)); - if (strlen(Cmd) == 0 || cmdp == 'h') return usage_lf_paradox_sim(); - uint32_t facilitycode = 0, cardnumber = 0, fc = 0, cn = 0; + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf paradox sim", + "Enables simulation of paradox card with specified card number.\n" + "Simulation runs until the button is pressed or another USB command is issued.", + "lf paradox sim --raw 0f55555695596a6a9999a59a" + ); - uint8_t bs[96]; - memset(bs, 0x00, sizeof(bs)); + void *argtable[] = { + arg_param_begin, + arg_str0("r", "raw", "", " raw hex data. 12 bytes"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + int raw_len = 0; + // skip first block, 3*4 = 12 bytes left + uint8_t raw[12] = {0}; + CLIGetHexWithReturn(ctx, 1, raw, &raw_len); + CLIParserFree(ctx); + + if (raw_len != 12) { + PrintAndLogEx(ERR, "Data must be 12 bytes (24 HEX characters) %d", raw_len); + return PM3_EINVARG; + } + + PrintAndLogEx(SUCCESS, "Simulating Paradox - raw " _YELLOW_("%s"), sprint_hex_inrow(raw, sizeof(raw))); + + uint8_t bs[sizeof(raw) * 8]; + bytes_to_bytebits(raw, sizeof(raw), bs); // Paradox uses: fcHigh: 10, fcLow: 8, clk: 50, invert: 1 FSK2a - uint8_t clk = 50, invert = 1, high = 10, low = 8; - - if (sscanf(Cmd, "%u %u", &fc, &cn) != 2) return usage_lf_paradox_sim(); - - facilitycode = (fc & 0x000000FF); - cardnumber = (cn & 0x0000FFFF); - - // if ( GetParadoxBits(facilitycode, cardnumber, bs) != PM3_SUCCESS) { - // PrintAndLogEx(ERR, "Error with tag bitstream generation."); - // return 1; - // } - - PrintAndLogEx(NORMAL, "Simulating Paradox - Facility Code: %u, CardNumber: %u", facilitycode, cardnumber); + uint8_t clk = 50, high = 10, low = 8; lf_fsksim_t *payload = calloc(1, sizeof(lf_fsksim_t) + sizeof(bs)); payload->fchigh = high; payload->fclow = low; - payload->separator = invert; + payload->separator = 0; payload->clock = clk; memcpy(payload->data, bs, sizeof(bs)); @@ -302,15 +322,29 @@ static int CmdParadoxSim(const char *Cmd) { PrintAndLogEx(INFO, "Done"); if (resp.status != PM3_EOPABORTED) return resp.status; - return PM3_SUCCESS; -} + + return PM3_SUCCESS;} +/* + + if (sscanf(Cmd, "%u %u", &fc, &cn) != 2) return usage_lf_paradox_sim(); + + facilitycode = (fc & 0x000000FF); + cardnumber = (cn & 0x0000FFFF); + + // if ( GetParadoxBits(facilitycode, cardnumber, bs) != PM3_SUCCESS) { + // PrintAndLogEx(ERR, "Error with tag bitstream generation."); + // return 1; + // } + + PrintAndLogEx(NORMAL, "Simulating Paradox - Facility Code: %u, CardNumber: %u", facilitycode, cardnumber); + */ static command_t CommandTable[] = { - {"help", CmdHelp, AlwaysAvailable, "This help"}, - {"demod", CmdParadoxDemod, AlwaysAvailable, "Demodulate a Paradox FSK tag from the GraphBuffer"}, - {"read", CmdParadoxRead, IfPm3Lf, "Attempt to read and Extract tag data from the antenna"}, - {"clone", CmdParadoxClone, IfPm3Lf, "clone paradox tag to T55x7"}, - {"sim", CmdParadoxSim, IfPm3Lf, "simulate paradox tag"}, + {"help", CmdHelp, AlwaysAvailable, "This help"}, + {"demod", CmdParadoxDemod, AlwaysAvailable, "Demodulate a Paradox FSK tag from the GraphBuffer"}, + {"reader", CmdParadoxReader, IfPm3Lf, "Attempt to read and Extract tag data from the antenna"}, + {"clone", CmdParadoxClone, IfPm3Lf, "clone paradox tag"}, + {"sim", CmdParadoxSim, IfPm3Lf, "simulate paradox tag"}, {NULL, NULL, NULL, NULL} }; From 3145a36a81cdaf1fea9726bdeab3e90d51ec5e7c Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Thu, 26 Nov 2020 23:42:59 +0100 Subject: [PATCH 44/52] keys --- client/dictionaries/mfc_default_keys.dic | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/client/dictionaries/mfc_default_keys.dic b/client/dictionaries/mfc_default_keys.dic index b3bf93312..06e7a5348 100644 --- a/client/dictionaries/mfc_default_keys.dic +++ b/client/dictionaries/mfc_default_keys.dic @@ -1275,4 +1275,6 @@ AABAFFCC7612 # gamefactory # ozdilek # -17D071403C20 \ No newline at end of file +17D071403C20 +# +534F4C415249 \ No newline at end of file From 90509c6a82a37fc2855884a750666f7cb003741c Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Thu, 26 Nov 2020 23:43:43 +0100 Subject: [PATCH 45/52] lf pac - now uses cliparser, support continuous mode, and EM (untested) --- client/src/cmdlfpac.c | 251 ++++++++++++++++++++++++++---------------- 1 file changed, 156 insertions(+), 95 deletions(-) diff --git a/client/src/cmdlfpac.c b/client/src/cmdlfpac.c index 211ddbca0..ff543c7e8 100644 --- a/client/src/cmdlfpac.c +++ b/client/src/cmdlfpac.c @@ -25,37 +25,11 @@ #include "protocols.h" // t55xx defines #include "cmdlft55xx.h" // clone #include "parity.h" +#include "cmdlfem4x05.h" // +#include "cliparser.h" static int CmdHelp(const char *Cmd); -static int usage_lf_pac_clone(void) { - PrintAndLogEx(NORMAL, "clone a PAC/Stanley tag to a T55x7 tag."); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Usage: lf pac clone [h] [c ] [b ]"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h : this help"); - PrintAndLogEx(NORMAL, " c : 8 byte card ID"); - PrintAndLogEx(NORMAL, " b : raw hex data. 16 bytes max"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" lf pac clone c CD4F5552 ")); - PrintAndLogEx(NORMAL, _YELLOW_(" lf pac clone b FF2049906D8511C593155B56D5B2649F ")); - return PM3_SUCCESS; -} -static int usage_lf_pac_sim(void) { - PrintAndLogEx(NORMAL, "Enables simulation of PAC/Stanley card with specified card number."); - PrintAndLogEx(NORMAL, "Simulation runs until the button is pressed or another USB command is issued."); - PrintAndLogEx(NORMAL, "The card ID is 8 byte number. Larger values are truncated."); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Usage: lf pac sim "); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " : 8 byte PAC/Stanley card id"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" lf pac sim 12345678")); - return PM3_SUCCESS; -} - // PAC_8byte format: preamble (8 mark/idle bits), ascii STX (02), ascii '2' (32), ascii '0' (30), ascii bytes 0..7 (cardid), then xor checksum of cardid bytes // all bytes following 8 bit preamble are one start bit (0), 7 data bits (lsb first), odd parity bit, and one stop bit (1) static int demodbuf_to_pacid(uint8_t *src, const size_t src_size, uint8_t *dst, const size_t dst_size) { @@ -189,93 +163,180 @@ static int CmdPacDemod(const char *Cmd) { return demodPac(true); } -static int CmdPacRead(const char *Cmd) { - (void)Cmd; - lf_read(false, 4096 * 2 + 20); - return demodPac(true); +static int CmdPacReader(const char *Cmd) { + + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf pac reader", + "read a pac tag", + "lf pac reader -@ -> continuous reader mode" + ); + + void *argtable[] = { + arg_param_begin, + arg_lit0("@", NULL, "optional - continuous reader mode"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + bool cm = arg_get_lit(ctx, 1); + CLIParserFree(ctx); + + do { + lf_read(false, 4096 * 2 + 20); + demodPac(!cm); + } while (cm && !kbd_enter_pressed()); + + return PM3_SUCCESS; } static int CmdPacClone(const char *Cmd) { - uint32_t blocks[5]; - bool errors = false; - uint8_t cmdp = 0; - int datalen = 0; + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf pac clone", + "clone a PAC/Stanley tag to a T55x7, Q5/T5555 or EM4305/4469 tag.", + "lf pac clone --cn CD4F5552\n" + "lf pac clone --cn CD4F5552 --q5 -> encode for Q5/T5555 tag\n" + "lf pac clone --cn CD4F5552 --em -> encode for EM4305/4469\n" + "lf pac clone --raw FF2049906D8511C593155B56D5B2649F" + ); - while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { - switch (tolower(param_getchar(Cmd, cmdp))) { - case 'h': - return usage_lf_pac_clone(); - case 'c': { - // skip first block, 4*4 = 16 bytes left - uint8_t rawhex[16] = {0}; - char cardid[9]; - int res = param_getstr(Cmd, cmdp + 1, cardid, sizeof(cardid)); - if (res < 8) - errors = true; + void *argtable[] = { + arg_param_begin, + arg_str0(NULL, "cn", "", "8 byte PAC/Stanley card ID"), + arg_str0("r", "raw", "", "raw hex data. 16 bytes max"), + arg_lit0(NULL, "q5", "optional - specify writing to Q5/T5555 tag"), + arg_lit0(NULL, "em", "optional - specify writing to EM4305/4469 tag"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); - pacCardIdToRaw(rawhex, cardid); - for (uint8_t i = 1; i < ARRAYLEN(blocks); i++) { - blocks[i] = bytes_to_num(rawhex + ((i - 1) * 4), sizeof(uint32_t)); - } - cmdp += 2; - break; - } - case 'b': { - // skip first block, 4*4 = 16 bytes left - uint8_t rawhex[16] = {0}; - int res = param_gethex_to_eol(Cmd, cmdp + 1, rawhex, sizeof(rawhex), &datalen); - if (res != 0) - errors = true; + uint8_t cnstr[9]; + int cnlen = 9; + memset(cnstr, 0x00, sizeof(cnstr)); + CLIGetStrWithReturn(ctx, 1, cnstr, &cnlen); - for (uint8_t i = 1; i < ARRAYLEN(blocks); i++) { - blocks[i] = bytes_to_num(rawhex + ((i - 1) * 4), sizeof(uint32_t)); - } - cmdp += 2; - break; - } - default: - PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); - errors = true; - break; - } + // skip first block, 4*4 = 16 bytes left + int raw_len = 0; + uint8_t raw[16] = {0}; + CLIGetHexWithReturn(ctx, 2, raw, &raw_len); + + bool q5 = arg_get_lit(ctx, 3); + bool em = arg_get_lit(ctx, 4); + CLIParserFree(ctx); + + if (q5 && em) { + PrintAndLogEx(FAILED, "Can't specify both Q5 and EM4305 at the same time"); + return PM3_EINVARG; + } + if (cnlen && raw_len) { + PrintAndLogEx(FAILED, "Can't specify both CardID and raw hex at the same time"); + return PM3_EINVARG; } - if (errors || cmdp == 0) return usage_lf_pac_clone(); + if (cnlen && cnlen < 8) { + PrintAndLogEx(FAILED, "Card ID must be 8 or 9 hex digits (%d)", cnlen); + return PM3_EINVARG; + } - //Pac - compat mode, NRZ, data rate 32, 3 data blocks + if (cnlen == 8 || cnlen == 9) { + pacCardIdToRaw(raw, (char*)cnstr); + } + + uint32_t blocks[5]; + for (uint8_t i = 1; i < ARRAYLEN(blocks); i++) { + blocks[i] = bytes_to_num(raw + ((i - 1) * 4), sizeof(uint32_t)); + } + + // Pac - compat mode, NRZ, data rate 32, 3 data blocks blocks[0] = T55x7_MODULATION_DIRECT | T55x7_BITRATE_RF_32 | 4 << T55x7_MAXBLOCK_SHIFT; + char cardtype[16] = {"T55x7"}; + // Q5 + if (q5) { + blocks[0] = T5555_FIXED | T5555_MODULATION_DIRECT | T5555_SET_BITRATE(32) | 4 << T5555_MAXBLOCK_SHIFT; + snprintf(cardtype, sizeof(cardtype), "Q5/T5555"); + } - PrintAndLogEx(INFO, "Preparing to clone PAC/Stanley tag to T55x7 with raw hex"); + // EM4305 + if (em) { + blocks[0] = EM4305_PAC_CONFIG_BLOCK; + snprintf(cardtype, sizeof(cardtype), "EM4305/4469"); + } + + PrintAndLogEx(INFO, "Preparing to clone PAC/Stanley tag to " _YELLOW_("%s") " with raw hex " _GREEN_("%s") + , cardtype + , sprint_hex_inrow(raw, sizeof(raw)) + ); print_blocks(blocks, ARRAYLEN(blocks)); - int res = clone_t55xx_tag(blocks, ARRAYLEN(blocks)); + int res; + if (em) { + res = em4x05_clone_tag(blocks, ARRAYLEN(blocks), 0, false); + } else { + res = clone_t55xx_tag(blocks, ARRAYLEN(blocks)); + } PrintAndLogEx(SUCCESS, "Done"); - PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf pac read`") " to verify"); + PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf pac reader`") " to verify"); return res; } static int CmdPacSim(const char *Cmd) { - // NRZ sim. - char cardid[9] = { 0 }; - uint8_t rawBytes[16] = { 0 }; - uint32_t rawBlocks[4]; - char cmdp = tolower(param_getchar(Cmd, 0)); - if (strlen(Cmd) == 0 || cmdp == 'h') return usage_lf_pac_sim(); + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf pac sim", + "Enables simulation of PAC/Stanley card with specified card number.\n" + "Simulation runs until the button is pressed or another USB command is issued.\n" + "The card ID is 8 byte number. Larger values are truncated.", + "lf pac sim --cn CD4F5552\n" + "lf pac sim --raw FF2049906D8511C593155B56D5B2649F" + ); - int res = param_getstr(Cmd, 0, cardid, sizeof(cardid)); - if (res < 8) return usage_lf_pac_sim(); + void *argtable[] = { + arg_param_begin, + arg_str0(NULL, "cn", "", "8 byte PAC/Stanley card ID"), + arg_str0("r", "raw", "", "raw hex data. 16 bytes max"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); - uint8_t bs[128]; - pacCardIdToRaw(rawBytes, cardid); - for (size_t i = 0; i < ARRAYLEN(rawBlocks); i++) { - rawBlocks[i] = bytes_to_num(rawBytes + (i * sizeof(uint32_t)), sizeof(uint32_t)); - num_to_bytebits(rawBlocks[i], sizeof(uint32_t) * 8, bs + (i * sizeof(uint32_t) * 8)); + uint8_t cnstr[10]; + int cnlen = 9; + memset(cnstr, 0x00, sizeof(cnstr)); + CLIGetStrWithReturn(ctx, 1, cnstr, &cnlen); + + // skip first block, 4*4 = 16 bytes left + int raw_len = 0; + uint8_t raw[16] = {0}; + CLIGetHexWithReturn(ctx, 2, raw, &raw_len); + CLIParserFree(ctx); + + if (cnlen && raw_len) { + PrintAndLogEx(FAILED, "Can't specify both CardID and raw hex at the same time"); + return PM3_EINVARG; } - PrintAndLogEx(SUCCESS, "Simulating PAC/Stanley - ID " _YELLOW_("%s")" raw "_YELLOW_("%08X%08X%08X%08X"), cardid, rawBlocks[0], rawBlocks[1], rawBlocks[2], rawBlocks[3]); + if (cnlen && cnlen < 8) { + PrintAndLogEx(FAILED, "Card ID must be 8 or 9 hex digits (%d)", cnlen); + return PM3_EINVARG; + } + if (cnlen == 8 || cnlen == 9) { + pacCardIdToRaw(raw, (char*)cnstr); + } + uint8_t bs[128]; + for (size_t i = 0; i < 4; i++) { + uint32_t tmp = bytes_to_num(raw + (i * sizeof(uint32_t)), sizeof(uint32_t)); + num_to_bytebits(tmp, sizeof(uint32_t) * 8, bs + (i * sizeof(uint32_t) * 8)); + } + + if (cnlen == 8 || cnlen == 9) { + PrintAndLogEx(SUCCESS, "Simulating PAC/Stanley - ID " _YELLOW_("%s")" raw " _YELLOW_("%s") + , cnstr + , sprint_hex_inrow(raw, raw_len) + ); + } else { + PrintAndLogEx(SUCCESS, "Simulating PAC/Stanley - raw " _YELLOW_("%s"), sprint_hex_inrow(raw, raw_len)); + } + + // NRZ sim. lf_nrzsim_t *payload = calloc(1, sizeof(lf_nrzsim_t) + sizeof(bs)); payload->invert = 0; payload->separator = 0; @@ -297,11 +358,11 @@ static int CmdPacSim(const char *Cmd) { } static command_t CommandTable[] = { - {"help", CmdHelp, AlwaysAvailable, "This help"}, - {"demod", CmdPacDemod, AlwaysAvailable, "Demodulate a PAC tag from the GraphBuffer"}, - {"read", CmdPacRead, IfPm3Lf, "Attempt to read and extract tag data from the antenna"}, - {"clone", CmdPacClone, IfPm3Lf, "clone PAC tag to T55x7"}, - {"sim", CmdPacSim, IfPm3Lf, "simulate PAC tag"}, + {"help", CmdHelp, AlwaysAvailable, "This help"}, + {"demod", CmdPacDemod, AlwaysAvailable, "Demodulate a PAC tag from the GraphBuffer"}, + {"reader", CmdPacReader, IfPm3Lf, "Attempt to read and extract tag data from the antenna"}, + {"clone", CmdPacClone, IfPm3Lf, "clone PAC tag to T55x7"}, + {"sim", CmdPacSim, IfPm3Lf, "simulate PAC tag"}, {NULL, NULL, NULL, NULL} }; From 026a3022dcead898cb29ccb054d2fb063c697530 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Fri, 27 Nov 2020 09:39:36 +0100 Subject: [PATCH 46/52] solari --- client/dictionaries/mfc_default_keys.dic | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/client/dictionaries/mfc_default_keys.dic b/client/dictionaries/mfc_default_keys.dic index 06e7a5348..38c6e2503 100644 --- a/client/dictionaries/mfc_default_keys.dic +++ b/client/dictionaries/mfc_default_keys.dic @@ -1277,4 +1277,5 @@ AABAFFCC7612 # 17D071403C20 # -534F4C415249 \ No newline at end of file +534F4C415249 +534f4c303232 \ No newline at end of file From edb70fe524560a0f58ee040b968d33ea8f038d8d Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Fri, 27 Nov 2020 10:48:53 +0100 Subject: [PATCH 47/52] lf pac clone/sim - adapted output to always print cardid --- client/src/cmdlfpac.c | 49 ++++++++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/client/src/cmdlfpac.c b/client/src/cmdlfpac.c index ff543c7e8..fbc642850 100644 --- a/client/src/cmdlfpac.c +++ b/client/src/cmdlfpac.c @@ -32,7 +32,7 @@ static int CmdHelp(const char *Cmd); // PAC_8byte format: preamble (8 mark/idle bits), ascii STX (02), ascii '2' (32), ascii '0' (30), ascii bytes 0..7 (cardid), then xor checksum of cardid bytes // all bytes following 8 bit preamble are one start bit (0), 7 data bits (lsb first), odd parity bit, and one stop bit (1) -static int demodbuf_to_pacid(uint8_t *src, const size_t src_size, uint8_t *dst, const size_t dst_size) { +static int pac_buf_to_cardid(uint8_t *src, const size_t src_size, uint8_t *dst, const size_t dst_size) { const size_t byteLength = 10; // start bit, 7 data bits, parity bit, stop bit const size_t startIndex = 8 + (3 * byteLength) + 1; // skip 8 bits preamble, STX, '2', '0', and first start bit const size_t dataLength = 9; @@ -67,35 +67,33 @@ static int demodbuf_to_pacid(uint8_t *src, const size_t src_size, uint8_t *dst, return PM3_SUCCESS; } -/* // convert a 16 byte array of raw demod data (FF204990XX...) to 8 bytes of PAC_8byte ID // performs no parity or checksum validation -static void pacRawToCardId(uint8_t* outCardId, const uint8_t* rawBytes) { +static void pac_raw_to_cardid(const uint8_t* src, uint8_t *dst) { for (int i = 4; i < 12; i++) { uint8_t shift = 7 - (i + 3) % 4 * 2; size_t index = i + (i - 1) / 4; - outCardId[i - 4] = reflect8((((rawBytes[index] << 8) | (rawBytes[index + 1])) >> shift) & 0xFE); + dst[i - 4] = reflect8((((src[index] << 8) | (src[index + 1])) >> shift) & 0xFE); } } -*/ // convert 8 bytes of PAC_8byte ID to 16 byte array of raw data (FF204990XX...) -static void pacCardIdToRaw(uint8_t *outRawBytes, const char *cardId) { +static void pac_cardid_to_raw(const char *src, uint8_t *dst) { uint8_t idbytes[10]; // prepend PAC_8byte card type "20" idbytes[0] = '2'; idbytes[1] = '0'; for (size_t i = 0; i < 8; i++) - idbytes[i + 2] = toupper(cardId[i]); + idbytes[i + 2] = toupper(src[i]); // initialise array with start and stop bits for (size_t i = 0; i < 16; i++) - outRawBytes[i] = 0x40 >> (i + 3) % 5 * 2; + dst[i] = 0x40 >> (i + 3) % 5 * 2; - outRawBytes[0] = 0xFF; // mark + stop - outRawBytes[1] = 0x20; // start + reflect8(STX) + dst[0] = 0xFF; // mark + stop + dst[1] = 0x20; // start + reflect8(STX) uint8_t checksum = 0; for (size_t i = 2; i < 13; i++) { @@ -112,8 +110,8 @@ static void pacCardIdToRaw(uint8_t *outRawBytes, const char *cardId) { } pattern <<= shift; - outRawBytes[index] |= pattern >> 8 & 0xFF; - outRawBytes[index + 1] |= pattern & 0xFF; + dst[index] |= pattern >> 8 & 0xFF; + dst[index + 1] |= pattern & 0xFF; } } @@ -150,7 +148,7 @@ int demodPac(bool verbose) { const size_t idLen = 9; // 8 bytes + null terminator uint8_t cardid[idLen]; - int retval = demodbuf_to_pacid(DemodBuffer, DemodBufferLen, cardid, sizeof(cardid)); + int retval = pac_buf_to_cardid(DemodBuffer, DemodBufferLen, cardid, sizeof(cardid)); if (retval == PM3_SUCCESS) PrintAndLogEx(SUCCESS, "PAC/Stanley - Card: " _GREEN_("%s") ", Raw: %08X%08X%08X%08X", cardid, raw1, raw2, raw3, raw4); @@ -238,7 +236,9 @@ static int CmdPacClone(const char *Cmd) { } if (cnlen == 8 || cnlen == 9) { - pacCardIdToRaw(raw, (char*)cnstr); + pac_cardid_to_raw((char*)cnstr, raw); + } else { + pac_raw_to_cardid(raw, cnstr); } uint32_t blocks[5]; @@ -261,10 +261,12 @@ static int CmdPacClone(const char *Cmd) { snprintf(cardtype, sizeof(cardtype), "EM4305/4469"); } - PrintAndLogEx(INFO, "Preparing to clone PAC/Stanley tag to " _YELLOW_("%s") " with raw hex " _GREEN_("%s") + PrintAndLogEx(INFO, "Preparing to clone PAC/Stanley tag to " _YELLOW_("%s") " with ID " _GREEN_("%s") " raw " _GREEN_("%s") , cardtype + , cnstr , sprint_hex_inrow(raw, sizeof(raw)) ); + print_blocks(blocks, ARRAYLEN(blocks)); int res; @@ -319,22 +321,21 @@ static int CmdPacSim(const char *Cmd) { } if (cnlen == 8 || cnlen == 9) { - pacCardIdToRaw(raw, (char*)cnstr); + pac_cardid_to_raw((char*)cnstr, raw); + } else { + pac_raw_to_cardid(raw, cnstr); } + uint8_t bs[128]; for (size_t i = 0; i < 4; i++) { uint32_t tmp = bytes_to_num(raw + (i * sizeof(uint32_t)), sizeof(uint32_t)); num_to_bytebits(tmp, sizeof(uint32_t) * 8, bs + (i * sizeof(uint32_t) * 8)); } - if (cnlen == 8 || cnlen == 9) { - PrintAndLogEx(SUCCESS, "Simulating PAC/Stanley - ID " _YELLOW_("%s")" raw " _YELLOW_("%s") - , cnstr - , sprint_hex_inrow(raw, raw_len) - ); - } else { - PrintAndLogEx(SUCCESS, "Simulating PAC/Stanley - raw " _YELLOW_("%s"), sprint_hex_inrow(raw, raw_len)); - } + PrintAndLogEx(SUCCESS, "Simulating PAC/Stanley - ID " _YELLOW_("%s")" raw " _YELLOW_("%s") + , cnstr + , sprint_hex_inrow(raw, sizeof(raw)) + ); // NRZ sim. lf_nrzsim_t *payload = calloc(1, sizeof(lf_nrzsim_t) + sizeof(bs)); From b2544faaf8342144f46887261bfbe92370167563 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Fri, 27 Nov 2020 11:04:05 +0100 Subject: [PATCH 48/52] lf noralsy - now uses cliparser, contiuous mode, EM (untested) --- client/src/cmdlfnoralsy.c | 168 ++++++++++++++++++++++---------------- 1 file changed, 98 insertions(+), 70 deletions(-) diff --git a/client/src/cmdlfnoralsy.c b/client/src/cmdlfnoralsy.c index de62f2236..a49093ec8 100644 --- a/client/src/cmdlfnoralsy.c +++ b/client/src/cmdlfnoralsy.c @@ -8,52 +8,23 @@ // ASK/Manchester, STT, RF/32, 96 bits long (some bits unknown) //----------------------------------------------------------------------------- #include "cmdlfnoralsy.h" - #include #include #include - -#include "commonutil.h" // ARRAYLEN +#include "commonutil.h" // ARRAYLEN #include "cmdparser.h" // command_t #include "comms.h" #include "ui.h" #include "cmddata.h" #include "cmdlf.h" -#include "protocols.h" // for T55xx config register definitions -#include "lfdemod.h" // parityTest -#include "cmdlft55xx.h" // verifywrite +#include "protocols.h" // for T55xx config register definitions +#include "lfdemod.h" // parityTest +#include "cmdlft55xx.h" // verifywrite +#include "cmdlfem4x05.h" // +#include "cliparser.h" static int CmdHelp(const char *Cmd); -static int usage_lf_noralsy_clone(void) { - PrintAndLogEx(NORMAL, "clone a Noralsy tag to a T55x7 or Q5/T5555 tag."); - PrintAndLogEx(NORMAL, "Usage: lf noralsy clone [h] "); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h : This help"); - PrintAndLogEx(NORMAL, " : Noralsy card ID"); - PrintAndLogEx(NORMAL, " : Tag allocation year"); - PrintAndLogEx(NORMAL, " : specify writing to Q5/T5555 tag"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" lf noralsy clone 112233")); - return PM3_SUCCESS; -} - -static int usage_lf_noralsy_sim(void) { - PrintAndLogEx(NORMAL, "Enables simulation of Noralsy card with specified card number."); - PrintAndLogEx(NORMAL, "Simulation runs until the button is pressed or another USB command is issued."); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Usage: lf noralsy sim [h] "); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h : This help"); - PrintAndLogEx(NORMAL, " : Noralsy card ID"); - PrintAndLogEx(NORMAL, " : Tag allocation year"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" lf noralsy sim 112233")); - return PM3_SUCCESS; -} - static uint8_t noralsy_chksum(uint8_t *bits, uint8_t len) { uint8_t sum = 0; for (uint8_t i = 0; i < len; i += 4) @@ -136,28 +107,72 @@ static int CmdNoralsyDemod(const char *Cmd) { return demodNoralsy(true); } -static int CmdNoralsyRead(const char *Cmd) { - (void)Cmd; // Cmd is not used so far - lf_read(false, 8000); - return demodNoralsy(true); +static int CmdNoralsyReader(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf noralsy reader", + "read a noralsy tag", + "lf noralsy reader -@ -> continuous reader mode" + ); + + void *argtable[] = { + arg_param_begin, + arg_lit0("@", NULL, "optional - continuous reader mode"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + bool cm = arg_get_lit(ctx, 1); + CLIParserFree(ctx); + + do { + lf_read(false, 8000); + demodNoralsy(!cm); + } while (cm && !kbd_enter_pressed()); + return PM3_SUCCESS; } static int CmdNoralsyClone(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf noralsy clone", + "clone a noralsy tag to a T55x7, Q5/T5555 or EM4305/4469 tag.", + "lf noralsy clone --cn 112233\n" + "lf noralsy clone --cn 112233 --q5 -> encode for Q5/T5555 tag\n" + "lf noralsy clone --cn 112233 --em -> encode for EM4305/4469" + ); + + void *argtable[] = { + arg_param_begin, + arg_u64_1(NULL, "cn", "", "Noralsy card ID"), + arg_u64_0("y", "year", "", "tag allocation year"), + arg_lit0(NULL, "q5", "optional - specify writing to Q5/T5555 tag"), + arg_lit0(NULL, "em", "optional - specify writing to EM4305/4469 tag"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + uint32_t id = arg_get_u32_def(ctx, 1, 0); + uint16_t year = arg_get_u32_def(ctx, 2, 2000); + bool q5 = arg_get_lit(ctx, 3); + bool em = arg_get_lit(ctx, 4); + CLIParserFree(ctx); + + if (q5 && em) { + PrintAndLogEx(FAILED, "Can't specify both Q5 and EM4305 at the same time"); + return PM3_EINVARG; + } - uint16_t year = 0; - uint32_t id = 0; uint32_t blocks[4] = {T55x7_MODULATION_MANCHESTER | T55x7_BITRATE_RF_32 | T55x7_ST_TERMINATOR | 3 << T55x7_MAXBLOCK_SHIFT, 0, 0}; - - char cmdp = tolower(param_getchar(Cmd, 0)); - if (strlen(Cmd) == 0 || cmdp == 'h') return usage_lf_noralsy_clone(); - - id = param_get32ex(Cmd, 0, 0, 10); - year = param_get32ex(Cmd, 1, 2000, 10); - + char cardtype[16] = {"T55x7"}; //Q5 - bool q5 = tolower(param_getchar(Cmd, 2) == 'q'); - if (q5) + if (q5) { blocks[0] = T5555_FIXED | T5555_MODULATION_MANCHESTER | T5555_SET_BITRATE(32) | T5555_ST_TERMINATOR | 3 << T5555_MAXBLOCK_SHIFT; + snprintf(cardtype, sizeof(cardtype), "Q5/T5555"); + } + + // EM4305 + if (em) { + blocks[0] = EM4305_NORALSY_CONFIG_BLOCK; + snprintf(cardtype, sizeof(cardtype), "EM4305/4469"); + } uint8_t *bits = calloc(96, sizeof(uint8_t)); if (getnoralsyBits(id, year, bits) != PM3_SUCCESS) { @@ -172,36 +187,50 @@ static int CmdNoralsyClone(const char *Cmd) { free(bits); - PrintAndLogEx(INFO, "Preparing to clone Noralsy to " _YELLOW_("%s") " with CardId: %u", (q5) ? "Q5/T5555" : "T55x7", id); + PrintAndLogEx(INFO, "Preparing to clone Noralsy to " _YELLOW_("%s") " with Card id: " _GREEN_("%u"), cardtype, id); print_blocks(blocks, ARRAYLEN(blocks)); - int res = clone_t55xx_tag(blocks, ARRAYLEN(blocks)); + int res; + if (em) { + res = em4x05_clone_tag(blocks, ARRAYLEN(blocks), 0, false); + } else { + res = clone_t55xx_tag(blocks, ARRAYLEN(blocks)); + } PrintAndLogEx(SUCCESS, "Done"); - PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf noralsy read`") " to verify"); + PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf noralsy reader`") " to verify"); return res; } static int CmdNoralsySim(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf noralsy sim", + "Enables simulation of Noralsy card with specified card number.\n" + "Simulation runs until the button is pressed or another USB command is issued.\n", + "lf noralsy sim --cn 1337\n" + "lf noralsy sim --cn 1337 --year 2010" + ); + + void *argtable[] = { + arg_param_begin, + arg_u64_1(NULL, "cn", "", "Noralsy card ID"), + arg_u64_0("y", "year", "", "tag allocation year"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + uint32_t id = arg_get_u32_def(ctx, 1, 0); + uint16_t year = arg_get_u32_def(ctx, 2, 2000); + CLIParserFree(ctx); + uint8_t bs[96]; memset(bs, 0, sizeof(bs)); - uint16_t year = 0; - uint32_t id = 0; - - char cmdp = tolower(param_getchar(Cmd, 0)); - if (strlen(Cmd) == 0 || cmdp == 'h') - return usage_lf_noralsy_sim(); - - id = param_get32ex(Cmd, 0, 0, 10); - year = param_get32ex(Cmd, 1, 2000, 10); - if (getnoralsyBits(id, year, bs) != PM3_SUCCESS) { PrintAndLogEx(ERR, "Error with tag bitstream generation."); return PM3_ESOFT; } - PrintAndLogEx(SUCCESS, "Simulating Noralsy - CardId: %u", id); + PrintAndLogEx(SUCCESS, "Simulating Noralsy - CardId: " _YELLOW_("%u") " year " _YELLOW_("%u"), id, year); lf_asksim_t *payload = calloc(1, sizeof(lf_asksim_t) + sizeof(bs)); payload->encoding = 1; @@ -225,11 +254,11 @@ static int CmdNoralsySim(const char *Cmd) { } static command_t CommandTable[] = { - {"help", CmdHelp, AlwaysAvailable, "This help"}, - {"demod", CmdNoralsyDemod, AlwaysAvailable, "Demodulate an Noralsy tag from the GraphBuffer"}, - {"read", CmdNoralsyRead, IfPm3Lf, "Attempt to read and extract tag data from the antenna"}, - {"clone", CmdNoralsyClone, IfPm3Lf, "clone Noralsy tag to T55x7 or Q5/T5555"}, - {"sim", CmdNoralsySim, IfPm3Lf, "simulate Noralsy tag"}, + {"help", CmdHelp, AlwaysAvailable, "This help"}, + {"demod", CmdNoralsyDemod, AlwaysAvailable, "Demodulate an Noralsy tag from the GraphBuffer"}, + {"reader", CmdNoralsyReader, IfPm3Lf, "Attempt to read and extract tag data from the antenna"}, + {"clone", CmdNoralsyClone, IfPm3Lf, "clone Noralsy tag to T55x7 or Q5/T5555"}, + {"sim", CmdNoralsySim, IfPm3Lf, "simulate Noralsy tag"}, {NULL, NULL, NULL, NULL} }; @@ -272,7 +301,6 @@ int getnoralsyBits(uint32_t id, uint16_t year, uint8_t *bits) { return PM3_SUCCESS; } -// by iceman // find Noralsy preamble in already demoded data int detectNoralsy(uint8_t *dest, size_t *size) { if (*size < 96) return -1; //make sure buffer has data From e57e6908cf3f45eb9c7b607739d9dc69c856646b Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Fri, 27 Nov 2020 13:58:29 +0100 Subject: [PATCH 49/52] textual --- doc/commands.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/doc/commands.md b/doc/commands.md index 770d9f4c6..bc0855668 100644 --- a/doc/commands.md +++ b/doc/commands.md @@ -38,6 +38,7 @@ Check column "offline" for their availability. |`analyse nuid `|Y |`create NUID from 7byte UID` |`analyse demodbuff `|Y |`Load binary string to demodbuffer` |`analyse freq `|Y |`Calc wave lengths` +|`analyse foo `|Y |`muxer` ### data @@ -772,7 +773,7 @@ Check column "offline" for their availability. |------- |------- |----------- |`lf nexwatch help `|Y |`This help` |`lf nexwatch demod `|Y |`Demodulate a NexWatch tag (nexkey, quadrakey) from the GraphBuffer` -|`lf nexwatch read `|N |`Attempt to Read and Extract tag data from the antenna` +|`lf nexwatch reader `|N |`Attempt to Read and Extract tag data from the antenna` |`lf nexwatch clone `|N |`clone NexWatch tag to T55x7` |`lf nexwatch sim `|N |`simulate NexWatch tag` @@ -785,7 +786,7 @@ Check column "offline" for their availability. |------- |------- |----------- |`lf noralsy help `|Y |`This help` |`lf noralsy demod `|Y |`Demodulate an Noralsy tag from the GraphBuffer` -|`lf noralsy read `|N |`Attempt to read and extract tag data from the antenna` +|`lf noralsy reader `|N |`Attempt to read and extract tag data from the antenna` |`lf noralsy clone `|N |`clone Noralsy tag to T55x7 or Q5/T5555` |`lf noralsy sim `|N |`simulate Noralsy tag` @@ -798,7 +799,7 @@ Check column "offline" for their availability. |------- |------- |----------- |`lf pac help `|Y |`This help` |`lf pac demod `|Y |`Demodulate a PAC tag from the GraphBuffer` -|`lf pac read `|N |`Attempt to read and extract tag data from the antenna` +|`lf pac reader `|N |`Attempt to read and extract tag data from the antenna` |`lf pac clone `|N |`clone PAC tag to T55x7` |`lf pac sim `|N |`simulate PAC tag` @@ -811,8 +812,8 @@ Check column "offline" for their availability. |------- |------- |----------- |`lf paradox help `|Y |`This help` |`lf paradox demod `|Y |`Demodulate a Paradox FSK tag from the GraphBuffer` -|`lf paradox read `|N |`Attempt to read and Extract tag data from the antenna` -|`lf paradox clone `|N |`clone paradox tag to T55x7` +|`lf paradox reader `|N |`Attempt to read and Extract tag data from the antenna` +|`lf paradox clone `|N |`clone paradox tag` |`lf paradox sim `|N |`simulate paradox tag` @@ -836,7 +837,7 @@ Check column "offline" for their availability. |------- |------- |----------- |`lf presco help `|Y |`This help` |`lf presco demod `|Y |`demodulate Presco tag from the GraphBuffer` -|`lf presco read `|N |`Attempt to read and Extract tag data` +|`lf presco reader `|N |`Attempt to read and Extract tag data` |`lf presco clone `|N |`clone presco tag to T55x7 or Q5/T5555` |`lf presco sim `|N |`simulate presco tag` From e4bbd30f4515b7e501e8be530bd6c31625b4383a Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Fri, 27 Nov 2020 17:54:34 +0100 Subject: [PATCH 50/52] lf nexwatch - now uses cliparser, support continuous mode, EM (untested) --- client/src/cmdlfnexwatch.c | 386 +++++++++++++++++++------------------ 1 file changed, 203 insertions(+), 183 deletions(-) diff --git a/client/src/cmdlfnexwatch.c b/client/src/cmdlfnexwatch.c index 8eb44c39b..3224e94a7 100644 --- a/client/src/cmdlfnexwatch.c +++ b/client/src/cmdlfnexwatch.c @@ -9,20 +9,21 @@ //----------------------------------------------------------------------------- #include "cmdlfnexwatch.h" -#include // PRIu +#include // PRIu #include -#include // tolower -#include // free, alloc - -#include "commonutil.h" // ARRAYLEN -#include "cmdparser.h" // command_t +#include // tolower +#include // free, alloc +#include "commonutil.h" // ARRAYLEN +#include "cmdparser.h" // command_t #include "comms.h" #include "ui.h" -#include "cmddata.h" // preamblesearch +#include "cmddata.h" // preamblesearch #include "cmdlf.h" #include "lfdemod.h" -#include "protocols.h" // t55xx defines -#include "cmdlft55xx.h" // clone.. +#include "protocols.h" // t55xx defines +#include "cmdlft55xx.h" // clone.. +#include "cmdlfem4x05.h" // +#include "cliparser.h" typedef enum { SCRAMBLE, @@ -31,49 +32,6 @@ typedef enum { static int CmdHelp(const char *Cmd); -static int usage_lf_nexwatch_clone(void) { - PrintAndLogEx(NORMAL, "clone a Nexwatch tag to a T55x7 tag."); - PrintAndLogEx(NORMAL, "You can use raw hex values or create a credential based on id, mode"); - PrintAndLogEx(NORMAL, "and type of credential (Nexkey/Quadrakey)"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Usage: lf nexwatch clone [h] [b ] [c ] [m ] [n|q]"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h : this help"); - PrintAndLogEx(NORMAL, " r : raw hex data. 12 bytes max"); - PrintAndLogEx(NORMAL, " c : card id (decimal)"); - PrintAndLogEx(NORMAL, " m : mode (decimal) (0-15, defaults to 1)"); - PrintAndLogEx(NORMAL, " n : Nexkey credential"); - PrintAndLogEx(NORMAL, " q : Quadrakey credential"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" lf nexwatch clone r 5600000000213C9F8F150C")); - PrintAndLogEx(NORMAL, _YELLOW_(" lf nexwatch clone c 521512301 m 1 n") " -- Nexkey credential"); - PrintAndLogEx(NORMAL, _YELLOW_(" lf nexwatch clone c 521512301 m 1 q") " -- Quadrakey credential"); - return PM3_SUCCESS; -} - -static int usage_lf_nexwatch_sim(void) { - PrintAndLogEx(NORMAL, "Enables simulation of Nexwatch card"); - PrintAndLogEx(NORMAL, "You can use raw hex values or create a credential based on id, mode"); - PrintAndLogEx(NORMAL, "and type of credential (Nexkey/Quadrakey)"); - PrintAndLogEx(NORMAL, "Simulation runs until the button is pressed or another USB command is issued."); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Usage: lf nexwatch sim [h] [c ] [m ] [n|q]"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h : this help"); - PrintAndLogEx(NORMAL, " r : raw hex data. 16 bytes max"); - PrintAndLogEx(NORMAL, " c : card id (decimal)"); - PrintAndLogEx(NORMAL, " m : mode (decimal) (0-15, defaults to 1)"); - PrintAndLogEx(NORMAL, " n : Nexkey credential"); - PrintAndLogEx(NORMAL, " q : Quadrakey credential"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" lf nexwatch sim r 5600000000213C9F8F150C")); - PrintAndLogEx(NORMAL, _YELLOW_(" lf nexwatch sim c 521512301 m 1 n") " -- Nexkey credential"); - PrintAndLogEx(NORMAL, _YELLOW_(" lf nexwatch sim c 521512301 m 1 q") " -- Quadrakey credential"); - return PM3_SUCCESS; -} - // scramble parity (1234) -> (4231) static uint8_t nexwatch_parity_swap(uint8_t parity) { uint8_t a = (((parity >> 3) & 1)); @@ -263,168 +221,230 @@ static int CmdNexWatchDemod(const char *Cmd) { return demodNexWatch(true); } -//by marshmellow -//see ASKDemod for what args are accepted -static int CmdNexWatchRead(const char *Cmd) { - (void)Cmd; - lf_read(false, 20000); - return demodNexWatch(true); +static int CmdNexWatchReader(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf nexwatch reader", + "read a nexwatch tag", + "lf nexwatch reader -@ -> continuous reader mode" + ); + + void *argtable[] = { + arg_param_begin, + arg_lit0("@", NULL, "optional - continuous reader mode"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + bool cm = arg_get_lit(ctx, 1); + CLIParserFree(ctx); + + do { + lf_read(false, 20000); + demodNexWatch(!cm); + } while (cm && !kbd_enter_pressed()); + return PM3_SUCCESS; } static int CmdNexWatchClone(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf nexwatch clone", + "clone a Nexwatch tag to a T55x7, Q5/T5555 or EM4305/4469 tag.\n" + "You can use raw hex values or create a credential based on id, mode\n" + "and type of credential (Nexkey / Quadrakey)", + "lf nexwatch clone --raw 5600000000213C9F8F150C00\n" + "lf nexwatch clone --cn 521512301 -m 1 -n -> Nexkey credential\n" + "lf nexwatch clone --cn 521512301 -m 1 -q -> Quadrakey credential\n" + ); - // 56000000 00213C9F 8F150C00 - uint32_t blocks[4]; - bool use_raw = false; - bool errors = false; - uint8_t cmdp = 0; - int datalen = 0; - uint8_t magic = 0xBE; - uint32_t cn = 0; - uint8_t rawhex[12] = {0x56, 0}; + void *argtable[] = { + arg_param_begin, + arg_str0("r", "raw", "", "raw hex data. 12 bytes"), + arg_u64_0(NULL, "cn", "", "card id"), + arg_u64_0("m", "mode", "", "mode (decimal) (0-15, defaults to 1)"), + arg_lit0("n", NULL, "Nexkey credential"), + arg_lit0("q", NULL, "Quadrakey credential"), + arg_lit0(NULL, "q5", "optional - specify writing to Q5/T5555 tag"), + arg_lit0(NULL, "em", "optional - specify writing to EM4305/4469 tag"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); - while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { - switch (tolower(param_getchar(Cmd, cmdp))) { - case 'h': - return usage_lf_nexwatch_clone(); - case 'r': { - int res = param_gethex_to_eol(Cmd, cmdp + 1, rawhex, sizeof(rawhex), &datalen); - if (res != 0) - errors = true; + int raw_len = 0; + // skip first block, 3*4 = 12 bytes left + uint8_t raw[12] = {0x56, 0}; + CLIGetHexWithReturn(ctx, 1, raw, &raw_len); - use_raw = true; - cmdp += 2; - break; - } - case 'c': { - cn = param_get32ex(Cmd, cmdp + 1, 0, 10); - uint32_t scrambled; - nexwatch_scamble(SCRAMBLE, &cn, &scrambled); - num_to_bytes(scrambled, 4, rawhex + 5); - cmdp += 2; - break; - } - case 'm': { - uint8_t mode = param_get8ex(Cmd, cmdp + 1, 1, 10); - mode &= 0x0F; - rawhex[9] |= (mode << 4); - cmdp += 2; - break; - } - case 'n': { - magic = 0x88; - cmdp++; - break; - } - case 'q': { - magic = 0xBE; - cmdp++; - break; - } - default: - PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); - errors = true; - break; - } + uint32_t cn = arg_get_u32_def(ctx, 2, -1); + uint32_t mode = arg_get_u32_def(ctx, 3, -1); + bool use_nexkey = arg_get_lit(ctx, 4); + bool use_quadrakey = arg_get_lit(ctx, 5); + bool q5 = arg_get_lit(ctx, 6); + bool em = arg_get_lit(ctx, 7); + CLIParserFree(ctx); + + if (use_nexkey && use_quadrakey) { + PrintAndLogEx(FAILED, "Can't specify both Nexkey and Quadrakey at the same time"); + return PM3_EINVARG; } - if (errors || cmdp == 0) return usage_lf_nexwatch_clone(); + if (q5 && em) { + PrintAndLogEx(FAILED, "Can't specify both Q5 and EM4305 at the same time"); + return PM3_EINVARG; + } + + // 56000000 00213C9F 8F150C00 + bool use_raw = (raw_len != 0); + + if (use_raw && cn != -1) { + PrintAndLogEx(FAILED, "Can't specify both Raw and Card id at the same time"); + return PM3_EINVARG; + } + + if (cn != -1) { + uint32_t scrambled; + nexwatch_scamble(SCRAMBLE, &cn, &scrambled); + num_to_bytes(scrambled, 4, raw + 5); + } + + if (mode != -1) { + if (mode > 15) { + mode = 1; + } + mode &= 0x0F; + raw[9] |= (mode << 4); + } + + uint8_t magic = 0xBE; + if (use_nexkey) + magic = 0x88; + + if (use_quadrakey) + magic = 0xBE; + + uint32_t blocks[4]; //Nexwatch - compat mode, PSK, data rate 40, 3 data blocks blocks[0] = T55x7_MODULATION_PSK1 | T55x7_BITRATE_RF_32 | 3 << T55x7_MAXBLOCK_SHIFT; + char cardtype[16] = {"T55x7"}; + // Q5 + if (q5) { + blocks[0] = T5555_FIXED | T5555_MODULATION_MANCHESTER | T5555_SET_BITRATE(64) | T5555_ST_TERMINATOR | 3 << T5555_MAXBLOCK_SHIFT; + snprintf(cardtype, sizeof(cardtype), "Q5/T5555"); + } + + // EM4305 + if (em) { + blocks[0] = EM4305_NEXWATCH_CONFIG_BLOCK; + snprintf(cardtype, sizeof(cardtype), "EM4305/4469"); + } if (use_raw == false) { - uint8_t parity = nexwatch_parity(rawhex + 5) & 0xF; - rawhex[9] |= parity; - rawhex[10] |= nexwatch_checksum(magic, cn, parity); + uint8_t parity = nexwatch_parity(raw + 5) & 0xF; + raw[9] |= parity; + raw[10] |= nexwatch_checksum(magic, cn, parity); } for (uint8_t i = 1; i < ARRAYLEN(blocks); i++) { - blocks[i] = bytes_to_num(rawhex + ((i - 1) * 4), sizeof(uint32_t)); + blocks[i] = bytes_to_num(raw + ((i - 1) * 4), sizeof(uint32_t)); } - PrintAndLogEx(INFO, "Preparing to clone NexWatch to T55x7 with raw hex"); + PrintAndLogEx(INFO, "Preparing to clone NexWatch to " _YELLOW_("%s") " raw " _YELLOW_("%s"), cardtype, sprint_hex_inrow(raw, sizeof(raw))); print_blocks(blocks, ARRAYLEN(blocks)); - int res = clone_t55xx_tag(blocks, ARRAYLEN(blocks)); + int res; + if (em) { + res = em4x05_clone_tag(blocks, ARRAYLEN(blocks), 0, false); + } else { + res = clone_t55xx_tag(blocks, ARRAYLEN(blocks)); + } PrintAndLogEx(SUCCESS, "Done"); - PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf nexwatch read`") " to verify"); + PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf nexwatch reader`") " to verify"); return res; } static int CmdNexWatchSim(const char *Cmd) { - uint8_t cmdp = 0; - bool errors = false; - bool use_raw = false; - uint8_t rawhex[12] = {0x56, 0}; - int rawlen = sizeof(rawhex); - uint8_t magic = 0xBE; - uint32_t cn = 0; - uint8_t bs[128]; - memset(bs, 0, sizeof(bs)); + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf nexwatch sim", + "Enables simulation of secura card with specified card number.\n" + "Simulation runs until the button is pressed or another USB command is issued.\n" + "You can use raw hex values or create a credential based on id, mode\n" + "and type of credential (Nexkey/Quadrakey)", + "lf nexwatch sim --raw 5600000000213C9F8F150C00\n" + "lf nexwatch sim --cn 521512301 -m 1 -n -> Nexkey credential\n" + "lf nexwatch sim --cn 521512301 -m 1 -q -> Quadrakey credential" + ); - while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { - switch (tolower(param_getchar(Cmd, cmdp))) { - case 'h': - return usage_lf_nexwatch_clone(); - case 'r': { - int res = param_gethex_to_eol(Cmd, cmdp + 1, rawhex, sizeof(rawhex), &rawlen); - if (res != 0) - errors = true; + void *argtable[] = { + arg_param_begin, + arg_str0("r", "raw", "", "raw hex data. 12 bytes"), + arg_u64_0(NULL, "cn", "", "card id"), + arg_u64_0("m", "mode", "", "mode (decimal) (0-15, defaults to 1)"), + arg_lit0("n", NULL, "Nexkey credential"), + arg_lit0("q", NULL, "Quadrakey credential"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); - use_raw = true; - cmdp += 2; - break; - } - case 'c': { - cn = param_get32ex(Cmd, cmdp + 1, 0, 10); - uint32_t scrambled; - nexwatch_scamble(SCRAMBLE, &cn, &scrambled); - num_to_bytes(scrambled, 4, rawhex + 5); - cmdp += 2; - break; - } - case 'm': { - uint8_t mode = param_get8ex(Cmd, cmdp + 1, 1, 10); - mode &= 0x0F; - rawhex[9] |= (mode << 4); - cmdp += 2; - break; - } - case 'n': { - magic = 0x88; - cmdp++; - break; - } - case 'q': { - magic = 0xBE; - cmdp++; - break; - } - default: - PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); - errors = true; - break; - } + int raw_len = 0; + // skip first block, 3*4 = 12 bytes left + uint8_t raw[12] = {0x56, 0}; + CLIGetHexWithReturn(ctx, 1, raw, &raw_len); + + uint32_t cn = arg_get_u32_def(ctx, 2, -1); + uint32_t mode = arg_get_u32_def(ctx, 3, -1); + bool use_nexkey = arg_get_lit(ctx, 4); + bool use_quadrakey = arg_get_lit(ctx, 5); + CLIParserFree(ctx); + + if (use_nexkey && use_quadrakey) { + PrintAndLogEx(FAILED, "Can't specify both Nexkey and Quadrakey at the same time"); + return PM3_EINVARG; } - if (errors || cmdp == 0) return usage_lf_nexwatch_sim(); + bool use_raw = (raw_len != 0); + + if (use_raw && cn != -1) { + PrintAndLogEx(FAILED, "Can't specify both Raw and Card id at the same time"); + return PM3_EINVARG; + } + + if (cn != -1) { + uint32_t scrambled; + nexwatch_scamble(SCRAMBLE, &cn, &scrambled); + num_to_bytes(scrambled, 4, raw + 5); + } + + if (mode != -1) { + if (mode > 15) { + mode = 1; + } + mode &= 0x0F; + raw[9] |= (mode << 4); + } + + uint8_t magic = 0xBE; + if (use_nexkey) + magic = 0x88; + + if (use_quadrakey) + magic = 0xBE; if (use_raw == false) { - uint8_t parity = nexwatch_parity(rawhex + 5) & 0xF; - rawhex[9] |= parity; - rawhex[10] |= nexwatch_checksum(magic, cn, parity); + uint8_t parity = nexwatch_parity(raw + 5) & 0xF; + raw[9] |= parity; + raw[10] |= nexwatch_checksum(magic, cn, parity); } - // hex to bits. - uint32_t rawblocks[3]; - for (size_t i = 0; i < ARRAYLEN(rawblocks); i++) { - rawblocks[i] = bytes_to_num(rawhex + (i * sizeof(uint32_t)), sizeof(uint32_t)); - num_to_bytebits(rawblocks[i], sizeof(uint32_t) * 8, bs + (i * sizeof(uint32_t) * 8)); + uint8_t bs[96]; + memset(bs, 0, sizeof(bs)); + + // hex to bits. (3 * 32 == 96) + for (size_t i = 0; i < 3; i++) { + uint32_t tmp = bytes_to_num(raw + (i * sizeof(uint32_t)), sizeof(uint32_t)); + num_to_bytebits(tmp, sizeof(uint32_t) * 8, bs + (i * sizeof(uint32_t) * 8)); } - PrintAndLogEx(SUCCESS, "Simulating NexWatch - raw: %s", sprint_hex_inrow(rawhex, rawlen)); + PrintAndLogEx(SUCCESS, "Simulating NexWatch - raw " _YELLOW_("%s"), sprint_hex_inrow(raw, sizeof(raw))); lf_psksim_t *payload = calloc(1, sizeof(lf_psksim_t) + sizeof(bs)); payload->carrier = 2; @@ -447,11 +467,11 @@ static int CmdNexWatchSim(const char *Cmd) { } static command_t CommandTable[] = { - {"help", CmdHelp, AlwaysAvailable, "This help"}, - {"demod", CmdNexWatchDemod, AlwaysAvailable, "Demodulate a NexWatch tag (nexkey, quadrakey) from the GraphBuffer"}, - {"read", CmdNexWatchRead, IfPm3Lf, "Attempt to Read and Extract tag data from the antenna"}, - {"clone", CmdNexWatchClone, IfPm3Lf, "clone NexWatch tag to T55x7"}, - {"sim", CmdNexWatchSim, IfPm3Lf, "simulate NexWatch tag"}, + {"help", CmdHelp, AlwaysAvailable, "This help"}, + {"demod", CmdNexWatchDemod, AlwaysAvailable, "Demodulate a NexWatch tag (nexkey, quadrakey) from the GraphBuffer"}, + {"reader", CmdNexWatchReader, IfPm3Lf, "Attempt to Read and Extract tag data from the antenna"}, + {"clone", CmdNexWatchClone, IfPm3Lf, "clone NexWatch tag to T55x7"}, + {"sim", CmdNexWatchSim, IfPm3Lf, "simulate NexWatch tag"}, {NULL, NULL, NULL, NULL} }; From 97b1562bad3d10f6dadda28d5c86bd10711db75b Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Fri, 27 Nov 2020 19:13:48 +0100 Subject: [PATCH 51/52] lf motorola - now uses cliparser, supports continuous mode, EM (untested) --- client/src/cmdlfmotorola.c | 132 ++++++++++++++++++++++++------------- 1 file changed, 85 insertions(+), 47 deletions(-) diff --git a/client/src/cmdlfmotorola.c b/client/src/cmdlfmotorola.c index 28b6bd39b..9f700c043 100644 --- a/client/src/cmdlfmotorola.c +++ b/client/src/cmdlfmotorola.c @@ -9,22 +9,20 @@ // PSK1, RF/32, 64 bits long, at 74 kHz //----------------------------------------------------------------------------- #include "cmdlfmotorola.h" - -#include //tolower - -#include "commonutil.h" // ARRAYLEN +#include // tolower +#include "commonutil.h" // ARRAYLEN #include "common.h" #include "cmdparser.h" // command_t #include "comms.h" #include "ui.h" #include "cmddata.h" #include "cmdlf.h" -#include "lfdemod.h" // preamble test -#include "protocols.h" // t55xx defines -#include "cmdlft55xx.h" // clone.. -#include "cmdlf.h" // cmdlfconfig -#include "cliparser.h" // cli parse input - +#include "lfdemod.h" // preamble test +#include "protocols.h" // t55xx defines +#include "cmdlft55xx.h" // clone.. +#include "cmdlf.h" // cmdlfconfig +#include "cliparser.h" // cli parse input +#include "cmdlfem4x05.h" // EM defines static int CmdHelp(const char *Cmd); @@ -124,7 +122,22 @@ static int CmdMotorolaDemod(const char *Cmd) { return demodMotorola(true); } -static int CmdMotorolaRead(const char *Cmd) { +static int CmdMotorolaReader(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf motorola reader", + "read a Motorola tag", + "lf motorola reader -@ -> continuous reader mode" + ); + + void *argtable[] = { + arg_param_begin, + arg_lit0("@", NULL, "optional - continuous reader mode"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + bool cm = arg_get_lit(ctx, 1); + CLIParserFree(ctx); + // Motorola Flexpass seem to work at 74 kHz // and take about 4400 samples to befor modulating sample_config sc = { @@ -138,61 +151,86 @@ static int CmdMotorolaRead(const char *Cmd) { }; lf_config(&sc); - // 64 * 32 * 2 * n-ish - lf_read(false, 5000); + do { + // 64 * 32 * 2 * n-ish + lf_read(false, 5000); + demodMotorola(!cm); + } while (cm && !kbd_enter_pressed()); // reset back to 125 kHz sc.divisor = LF_DIVISOR_125; sc.samples_to_skip = 0; lf_config(&sc); - return demodMotorola(true); + + return PM3_SUCCESS; } static int CmdMotorolaClone(const char *Cmd) { - - uint32_t blocks[3] = {0}; - uint8_t data[8]; - int datalen = 0; - CLIParserContext *ctx; CLIParserInit(&ctx, "lf motorola clone", - "clone Motorola UID to T55x7 or Q5/T5555 tag\n" + "clone Motorola UID to a T55x7, Q5/T5555 or EM4305/4469 tag.\n" "defaults to 64 bit format", - "lf motorola clone -r a0000000a0002021" + "lf motorola clone --raw a0000000a0002021\n" + "lf motorola clone --q5 --raw a0000000a0002021 -> encode for Q5/T5555 tag\n" + "lf motorola clone --em --raw a0000000a0002021 -> encode for EM4305/4469" ); void *argtable[] = { arg_param_begin, - arg_strx1("r", "raw", "", "raw bytes"), - arg_lit0("q", "Q5", "optional - specify writing to Q5/T5555 tag"), + arg_strx1("r", "raw", "", "raw hex bytes. 8 bytes"), + arg_lit0(NULL, "q5", "optional - specify writing to Q5/T5555 tag"), + arg_lit0(NULL, "em", "optional - specify writing to EM4305/4469 tag"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); - CLIGetHexWithReturn(ctx, 1, data, &datalen); - bool is_t5555 = arg_get_lit(ctx, 2); + + int raw_len = 0; + uint8_t raw[8]; + CLIGetHexWithReturn(ctx, 1, raw, &raw_len); + bool q5 = arg_get_lit(ctx, 2); + bool em = arg_get_lit(ctx, 3); CLIParserFree(ctx); - //TODO add selection of chip for Q5 or T55x7 + if (q5 && em) { + PrintAndLogEx(FAILED, "Can't specify both Q5 and EM4305 at the same time"); + return PM3_EINVARG; + } - PrintAndLogEx(INFO, "Target chip " _YELLOW_("%s"), (is_t5555) ? "Q5/T5555" : "T55x7"); + //TODO add selection of chip for Q5 or T55x7 + uint32_t blocks[3] = {0}; + + blocks[0] = T55x7_BITRATE_RF_32 | T55x7_MODULATION_PSK1 | (2 << T55x7_MAXBLOCK_SHIFT); + char cardtype[16] = {"T55x7"}; + // Q5 + if (q5) { + blocks[0] = T5555_FIXED | T5555_SET_BITRATE(32) | T5555_MODULATION_PSK1 | 2 << T5555_MAXBLOCK_SHIFT; + snprintf(cardtype, sizeof(cardtype), "Q5/T5555"); + } + + // EM4305 + if (em) { + blocks[0] = EM4305_MOTOROLA_CONFIG_BLOCK; + snprintf(cardtype, sizeof(cardtype), "EM4305/4469"); + } + + blocks[1] = bytes_to_num(raw, 4); + blocks[2] = bytes_to_num(raw + 4, 4); // config for Motorola 64 format (RF/32;PSK1 with RF/2; Maxblock=2) - PrintAndLogEx(INFO, "Preparing to clone Motorola 64bit tag"); - PrintAndLogEx(INFO, "Using raw " _GREEN_("%s"), sprint_hex_inrow(data, datalen)); - - if (is_t5555) - blocks[0] = T5555_FIXED | T5555_SET_BITRATE(32) | T5555_MODULATION_PSK1 | 2 << T5555_MAXBLOCK_SHIFT; - else - blocks[0] = T55x7_BITRATE_RF_32 | T55x7_MODULATION_PSK1 | (2 << T55x7_MAXBLOCK_SHIFT); - - - blocks[1] = bytes_to_num(data, 4); - blocks[2] = bytes_to_num(data + 4, 4); - + PrintAndLogEx(INFO, "Preparing to clone Motorola 64bit to " _YELLOW_("%s") " with raw " _GREEN_("%s") + , cardtype + , sprint_hex_inrow(raw, sizeof(raw)) + ); print_blocks(blocks, ARRAYLEN(blocks)); - int res = clone_t55xx_tag(blocks, ARRAYLEN(blocks)); + + int res; + if (em) { + res = em4x05_clone_tag(blocks, ARRAYLEN(blocks), 0, false); + } else { + res = clone_t55xx_tag(blocks, ARRAYLEN(blocks)); + } PrintAndLogEx(SUCCESS, "Done"); - PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf motorola read`") " to verify"); + PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf motorola reader`") " to verify"); return res; } @@ -205,11 +243,11 @@ static int CmdMotorolaSim(const char *Cmd) { } static command_t CommandTable[] = { - {"help", CmdHelp, AlwaysAvailable, "This help"}, - {"demod", CmdMotorolaDemod, AlwaysAvailable, "Demodulate an MOTOROLA tag from the GraphBuffer"}, - {"read", CmdMotorolaRead, IfPm3Lf, "Attempt to read and extract tag data from the antenna"}, - {"clone", CmdMotorolaClone, IfPm3Lf, "clone MOTOROLA tag to T55x7"}, - {"sim", CmdMotorolaSim, IfPm3Lf, "simulate MOTOROLA tag"}, + {"help", CmdHelp, AlwaysAvailable, "This help"}, + {"demod", CmdMotorolaDemod, AlwaysAvailable, "Demodulate an MOTOROLA tag from the GraphBuffer"}, + {"reader", CmdMotorolaReader, IfPm3Lf, "Attempt to read and extract tag data from the antenna"}, + {"clone", CmdMotorolaClone, IfPm3Lf, "clone MOTOROLA tag to T55x7"}, + {"sim", CmdMotorolaSim, IfPm3Lf, "simulate MOTOROLA tag"}, {NULL, NULL, NULL, NULL} }; @@ -266,5 +304,5 @@ int detectMotorola(uint8_t *dest, size_t *size) { } int readMotorolaUid(void) { - return (CmdMotorolaRead("") == PM3_SUCCESS); + return (CmdMotorolaReader("") == PM3_SUCCESS); } From 00f140683fa8b2a03b36755b5bdf0617d9ba9711 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Fri, 27 Nov 2020 19:41:25 +0100 Subject: [PATCH 52/52] lf keri - now supports continuous mode, EM (untested) --- client/src/cmdlfkeri.c | 134 +++++++++++++++++++++-------------------- 1 file changed, 69 insertions(+), 65 deletions(-) diff --git a/client/src/cmdlfkeri.c b/client/src/cmdlfkeri.c index c482a7941..93e8a2521 100644 --- a/client/src/cmdlfkeri.c +++ b/client/src/cmdlfkeri.c @@ -179,68 +179,56 @@ static int CmdKeriDemod(const char *Cmd) { return demodKeri(true); } -static int CmdKeriRead(const char *Cmd) { - (void)Cmd; // Cmd is not used so far - lf_read(false, 10000); - return demodKeri(true); -} - -static int CmdKeriClone(const char *Cmd) { - - bool q5 = false, em = false; - - uint8_t keritype[2] = {'i'}; // default to internalid - int typeLen = 0; - uint32_t fc = 0; - uint32_t cid = 0; - uint32_t internalid = 0; - uint32_t blocks[3] = { - T55x7_TESTMODE_DISABLED | - T55x7_X_MODE | - T55x7_MODULATION_PSK1 | - T55x7_PSKCF_RF_2 | - 2 << T55x7_MAXBLOCK_SHIFT, - 0, - 0 - }; - - // dynamic bitrate used - blocks[0] |= 0xF << 18; - +static int CmdKeriReader(const char *Cmd) { CLIParserContext *ctx; - CLIParserInit(&ctx, "lf keri clone", - "clone a KERI tag to a T55x7, Q5/T5555 or EM4305/4469 tag", - "lf keri clone -t i --id 12345\n" - "lf keri clone -t m --fc 6 --id 12345\n"); + CLIParserInit(&ctx, "lf keri reader", + "read a keri tag", + "lf keri reader -@ -> continuous reader mode" + ); + + void *argtable[] = { + arg_param_begin, + arg_lit0("@", NULL, "optional - continuous reader mode"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + bool cm = arg_get_lit(ctx, 1); + CLIParserFree(ctx); + + do { + lf_read(false, 10000); + demodKeri(!cm); + } while (cm && !kbd_enter_pressed()); + + return PM3_SUCCESS; +} + +static int CmdKeriClone(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf keri clone", + "clone a KERI tag to a T55x7, Q5/T5555 or EM4305/4469 tag", + "lf keri clone -t i --cn 12345 -> Internal ID\n" + "lf keri clone -t m --fc 6 --cn 12345 -> MS ID\n"); void *argtable[] = { arg_param_begin, - arg_lit0("q", "q5", "specify writing to Q5/T5555 tag"), arg_str0("t", "type", "", "Type m - MS, i - Internal ID"), arg_int0(NULL, "fc", "", "Facility Code"), - arg_int1(NULL, "id", "", "Keri ID"), - arg_lit0(NULL, "em", "specify writing to EM4305/4469 tag"), + arg_int1(NULL, "cn", "", "KERI card ID"), + arg_lit0(NULL, "q5", "specify writing to Q5/T5555 tag"), + arg_lit0(NULL, "em", "specify writing to EM4305/4469 tag"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); - char cardtype[16] = {"T55x7"}; - if (arg_get_lit(ctx, 1)) { - blocks[0] = T5555_FIXED | T5555_MODULATION_PSK1 | T5555_SET_BITRATE(32) | T5555_PSK_RF_2 | 2 << T5555_MAXBLOCK_SHIFT; - snprintf(cardtype, sizeof(cardtype), "Q5/T5555"); - q5 = true; - } - if (arg_get_lit(ctx, 5)) { - blocks[0] = EM4305_KERI_CONFIG_BLOCK; - snprintf(cardtype, sizeof(cardtype), "EM4305/4469"); - em = true; - } + uint8_t keritype[2] = {'i'}; // default to internalid + int typeLen = sizeof(keritype); + CLIGetStrWithReturn(ctx, 1, keritype, &typeLen); - typeLen = sizeof(keritype); - CLIGetStrWithReturn(ctx, 2, keritype, &typeLen); - - fc = arg_get_int_def(ctx, 3, 0); - cid = arg_get_int_def(ctx, 4, 0); + uint32_t fc = arg_get_int_def(ctx, 2, 0); + uint32_t cid = arg_get_int_def(ctx, 3, 0); + bool q5 = arg_get_lit(ctx, 4); + bool em = arg_get_lit(ctx, 5); CLIParserFree(ctx); if (q5 && em) { @@ -249,6 +237,7 @@ static int CmdKeriClone(const char *Cmd) { } // Setup card data/build internal id + uint32_t internalid = 0; switch (keritype[0]) { case 'i' : // Internal ID // MSB is ONE @@ -262,6 +251,24 @@ static int CmdKeriClone(const char *Cmd) { return PM3_EINVARG; } + uint32_t blocks[3]; + blocks[0] = T55x7_TESTMODE_DISABLED | T55x7_X_MODE | T55x7_MODULATION_PSK1 | T55x7_PSKCF_RF_2 | 2 << T55x7_MAXBLOCK_SHIFT; + // dynamic bitrate used + blocks[0] |= 0xF << 18; + + char cardtype[16] = {"T55x7"}; + + if (q5) { + blocks[0] = T5555_FIXED | T5555_MODULATION_PSK1 | T5555_SET_BITRATE(32) | T5555_PSK_RF_2 | 2 << T5555_MAXBLOCK_SHIFT; + snprintf(cardtype, sizeof(cardtype), "Q5/T5555"); + } + + if (em) { + blocks[0] = EM4305_KERI_CONFIG_BLOCK; + snprintf(cardtype, sizeof(cardtype), "EM4305/4469"); + } + + // Prepare and write to card // 3 LSB is ONE uint64_t data = ((uint64_t)internalid << 3) + 7; @@ -288,19 +295,18 @@ static int CmdKeriSim(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "lf keri sim", - "Enables simulation of KERI card with card number.", - "lf keri sim --id 112233" + "Enables simulation of KERI card with internal ID.\n" + "You supply a KERI card id and it will converted to a KERI internal ID.", + "lf keri sim --cn 112233" ); void *argtable[] = { arg_param_begin, - arg_int1(NULL, "id", "", "KERI Internal ID"), + arg_u64_1(NULL, "id", "", "KERI card ID"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); - - uint64_t internalid = arg_get_int_def(ctx, 1, 0); - + uint64_t internalid = arg_get_u64_def(ctx, 1, 0); CLIParserFree(ctx); internalid |= 0x80000000; @@ -314,7 +320,7 @@ static int CmdKeriSim(const char *Cmd) { bs[j++] = ((internalid >> i) & 1); } - PrintAndLogEx(SUCCESS, "Simulating KERI - Internal Id: %" PRIu64, internalid); + PrintAndLogEx(SUCCESS, "Simulating KERI - Internal Id " _YELLOW_("%" PRIu64), internalid); lf_psksim_t *payload = calloc(1, sizeof(lf_psksim_t) + sizeof(bs)); payload->carrier = 2; @@ -322,8 +328,6 @@ static int CmdKeriSim(const char *Cmd) { payload->clock = 32; memcpy(payload->data, bs, sizeof(bs)); - PrintAndLogEx(INFO, "Simulating"); - clearCommandBuffer(); SendCommandNG(CMD_LF_PSK_SIMULATE, (uint8_t *)payload, sizeof(lf_psksim_t) + sizeof(bs)); free(payload); @@ -338,11 +342,11 @@ static int CmdKeriSim(const char *Cmd) { } static command_t CommandTable[] = { - {"help", CmdHelp, AlwaysAvailable, "This help"}, - {"demod", CmdKeriDemod, AlwaysAvailable, "Demodulate an KERI tag from the GraphBuffer"}, - {"read", CmdKeriRead, IfPm3Lf, "Attempt to read and extract tag data from the antenna"}, - {"clone", CmdKeriClone, IfPm3Lf, "clone KERI tag to T55x7 or Q5/T5555"}, - {"sim", CmdKeriSim, IfPm3Lf, "simulate KERI tag"}, + {"help", CmdHelp, AlwaysAvailable, "This help"}, + {"demod", CmdKeriDemod, AlwaysAvailable, "Demodulate an KERI tag from the GraphBuffer"}, + {"reader", CmdKeriReader, IfPm3Lf, "Attempt to read and extract tag data from the antenna"}, + {"clone", CmdKeriClone, IfPm3Lf, "clone KERI tag to T55x7 or Q5/T5555"}, + {"sim", CmdKeriSim, IfPm3Lf, "simulate KERI tag"}, {NULL, NULL, NULL, NULL} };