diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index ba67cb144..5f522aed1 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -2275,6 +2275,49 @@ void SimulateIso14443aTagEx(uint8_t tagType, uint16_t flags, uint8_t *useruid, u EmSendCmd(cmd, sizeof(cmd)); p_response = NULL; + } else if (receivedCmd[0] == MFP_WRITEPERSO && len == 21 && (tagType == 15)) { + // cmd + 2 bytes block + value + 2 bytes crc + if (CheckCrc14A(receivedCmd, len)) { + uint16_t page = receivedCmd[1] | (receivedCmd[2] << 8); + if (page > 0x9005 || page < 0x9000) { + // send NACK 0x0 == invalid argument + EmSend4bit(CARD_NACK_IV); + } else { + // send ACK + EmSend4bit(CARD_ACK); + + Dbprintf("MFP Perso Key | %04X | %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X", + page, + receivedCmd[3], + receivedCmd[4], + receivedCmd[5], + receivedCmd[6], + receivedCmd[7], + receivedCmd[8], + receivedCmd[9], + receivedCmd[10], + receivedCmd[11], + receivedCmd[12], + receivedCmd[13], + receivedCmd[14], + receivedCmd[15], + receivedCmd[16], + receivedCmd[17], + receivedCmd[18] + ); + + numReads++; // Increment number of times reader requested a block + + if (exitAfterNReads > 0 && numReads == exitAfterNReads) { + finished = true; + } + } + } else { + // send NACK 0x1 == crc/parity error + EmSend4bit(CARD_NACK_PA); + } + p_response = NULL; + } else { // clear old dynamic responses @@ -3633,6 +3676,9 @@ void ReaderIso14443a(PacketCommandNG *c) { if ((param & ISO14A_CONNECT) == ISO14A_CONNECT) { iso14_pcb_blocknum = 0; + } + + if ((param & ISO14A_CLEARTRACE) == ISO14A_CLEARTRACE) { clear_trace(); } diff --git a/client/lualibs/read14a.lua b/client/lualibs/read14a.lua index 782fca6d5..223b6c306 100644 --- a/client/lualibs/read14a.lua +++ b/client/lualibs/read14a.lua @@ -31,6 +31,7 @@ local ISO14A_COMMAND = { ISO14A_SEND_CHAINING = 0x400, ISO14A_USE_ECP = 0x800, ISO14A_USE_MAGSAFE = 0x1000, + ISO14A_CLEARTRACE = 0x20000, } local ISO14443a_TYPES = {} @@ -90,7 +91,7 @@ local function read14443a(dont_disconnect, no_rats) command = Command:newMIX{ cmd = cmds.CMD_HF_ISO14443A_READER, - arg1 = ISO14A_COMMAND.ISO14A_CONNECT + arg1 = ISO14A_COMMAND.ISO14A_CONNECT + ISO14A_COMMAND.ISO14A_CLEARTRACE } if dont_disconnect then diff --git a/client/luascripts/hf_14a_read_ltocm.lua b/client/luascripts/hf_14a_read_ltocm.lua index 362868177..626080065 100644 --- a/client/luascripts/hf_14a_read_ltocm.lua +++ b/client/luascripts/hf_14a_read_ltocm.lua @@ -72,7 +72,7 @@ local function sendRaw(rawdata, options) local flags = lib14a.ISO14A_COMMAND.ISO14A_NO_DISCONNECT + lib14a.ISO14A_COMMAND.ISO14A_RAW if options.connect then - flags = flags + lib14a.ISO14A_COMMAND.ISO14A_CONNECT + flags = flags + lib14a.ISO14A_COMMAND.ISO14A_CONNECT + lib14a.ISO14A_COMMAND.ISO14A_CLEARTRACE end if options.no_select then diff --git a/client/luascripts/hf_mfu_ultra.lua b/client/luascripts/hf_mfu_ultra.lua index ed6a2b974..93dd5bcec 100644 --- a/client/luascripts/hf_mfu_ultra.lua +++ b/client/luascripts/hf_mfu_ultra.lua @@ -75,7 +75,7 @@ local function sendRaw(rawdata, options) end if options.connect then - flags = flags + lib14a.ISO14A_COMMAND.ISO14A_CONNECT + flags = flags + lib14a.ISO14A_COMMAND.ISO14A_CONNECT + lib14a.ISO14A_COMMAND.ISO14A_CLEARTRACE end if options.no_select then diff --git a/client/resources/aidlist.json b/client/resources/aidlist.json index 519557ed0..eb337dbf8 100644 --- a/client/resources/aidlist.json +++ b/client/resources/aidlist.json @@ -2217,11 +2217,30 @@ }, { "AID": "4F53452E5641532E3031", - "Vendor": "Apple, Google", + "Vendor": "Apple", "Country": "", - "Name": "Value-Added Services (OSE.VAS.01)", - "Description": "Used by Apple VAS and Google SmartTap", - "Type": "loyalty" + "Name": "Apple VAS (OSE.VAS.01)", + "Description": "Apple VAS", + "Type": "loyalty", + "ResponseContains": "50084170706c65506179" + }, + { + "AID": "4F53452E5641532E3031", + "Vendor": "Google", + "Country": "", + "Name": "Google Smart Tap (OSE.VAS.01)", + "Description": "Google Smart Tap", + "Type": "loyalty", + "ResponseContains": "500a416e64726f6964506179" + }, + { + "AID": "4F53452E5641532E3031", + "Vendor": "Samsung", + "Country": "", + "Name": "Samsung VAS (OSE.VAS.01)", + "Description": "Samsung VAS", + "Type": "loyalty", + "ResponseContains": "500d53616d73756e6757616c6c6574" }, { "AID": "A000000476D0000101", @@ -2239,6 +2258,14 @@ "Description": "", "Type": "loyalty" }, + { + "AID": "A000000870FC000000000034", + "Vendor": "Samsung", + "Country": "", + "Name": "Samsung VAS", + "Description": "", + "Type": "loyalty" + }, { "AID": "A0000002480400", "Vendor": "ISO/IEC JTC1/SC17", diff --git a/client/src/aidsearch.c b/client/src/aidsearch.c index 98607d199..65810c0b6 100644 --- a/client/src/aidsearch.c +++ b/client/src/aidsearch.c @@ -20,6 +20,7 @@ #include #include "fileutils.h" #include "pm3_cmd.h" +#include "util.h" static int openAIDFile(json_t **root, bool verbose) { json_error_t error; @@ -111,6 +112,27 @@ static bool aidCompare(const char *aidlarge, const char *aidsmall) { return false; } +static bool ResponseContainsMatch(const char *needle, const char *text) { + if (needle == NULL || text == NULL || needle[0] == '\0' || text[0] == '\0') { + return false; + } + + char *needle_lc = str_dup(needle); + char *text_lc = str_dup(text); + if (needle_lc == NULL || text_lc == NULL) { + free(needle_lc); + free(text_lc); + return false; + } + + str_lower(needle_lc); + str_lower(text_lc); + bool matched = (strstr(text_lc, needle_lc) != NULL); + free(needle_lc); + free(text_lc); + return matched; +} + bool AIDGetFromElm(json_t *data, uint8_t *aid, size_t aidmaxlen, int *aidlen) { *aidlen = 0; const char *hexaid = jsonStrGet(data, "AID"); @@ -124,7 +146,49 @@ bool AIDGetFromElm(json_t *data, uint8_t *aid, size_t aidmaxlen, int *aidlen) { return true; } +bool AIDSeenBefore(json_t *root, const uint8_t *aid, size_t aidlen, size_t before_index) { + if (root == NULL || aid == NULL || aidlen == 0) { + return false; + } + + size_t limit = before_index; + if (limit > json_array_size(root)) { + limit = json_array_size(root); + } + + for (size_t i = 0; i < limit; i++) { + json_t *data = AIDSearchGetElm(root, i); + if (data == NULL) { + continue; + } + + uint8_t prev_aid[200] = {0}; + int prev_aid_len = 0; + if ((AIDGetFromElm(data, prev_aid, sizeof(prev_aid), &prev_aid_len) == false) || (prev_aid_len <= 0)) { + continue; + } + + if ((size_t)prev_aid_len == aidlen && memcmp(prev_aid, aid, aidlen) == 0) { + return true; + } + } + + return false; +} + int PrintAIDDescription(json_t *xroot, char *aid, bool verbose) { + return PrintAIDDescriptionEx(xroot, aid, NULL, 0, verbose); +} + +int PrintAIDDescriptionBuf(json_t *root, uint8_t *aid, size_t aidlen, bool verbose) { + return PrintAIDDescription(root, sprint_hex_inrow(aid, aidlen), verbose); +} + +int PrintAIDDescriptionEx(json_t *xroot, char *aid, const uint8_t *response, size_t response_len, bool verbose) { + if (aid == NULL || aid[0] == '\0') { + return PM3_SUCCESS; + } + int retval = PM3_SUCCESS; json_t *root = xroot; @@ -135,50 +199,89 @@ int PrintAIDDescription(json_t *xroot, char *aid, bool verbose) { goto out; } - json_t *elm = NULL; + char *response_hex = NULL; + if (response != NULL && response_len > 0) { + if (response_len > ((SIZE_MAX - 1) / 2)) { + goto out; + } + size_t response_hexlen = (response_len * 2) + 1; + response_hex = calloc(response_hexlen, sizeof(char)); + if (response_hex == NULL) { + goto out; + } + hex_to_buffer((uint8_t *)response_hex, response, response_len, response_hexlen - 1, 0, 0, true); + } + + json_t *fallback_elm = NULL; + json_t *contains_elm = NULL; size_t maxaidlen = 0; + for (size_t elmindx = 0; elmindx < json_array_size(root); elmindx++) { json_t *data = AIDSearchGetElm(root, elmindx); if (data == NULL) { continue; } + const char *dictaid = jsonStrGet(data, "AID"); - if (aidCompare(aid, dictaid)) { // dictaid may be less length than requested aid - if (maxaidlen < strlen(dictaid) && strlen(dictaid) <= strlen(aid)) { - maxaidlen = strlen(dictaid); - elm = data; + if (dictaid == NULL) { + continue; + } + + if (!aidCompare(aid, dictaid)) { // dictaid may be less length than requested aid + continue; + } + + size_t dictaidlen = strlen(dictaid); + if (dictaidlen > strlen(aid)) { + continue; + } + + if (dictaidlen > maxaidlen) { + maxaidlen = dictaidlen; + fallback_elm = data; + contains_elm = NULL; + } else if (dictaidlen < maxaidlen) { + continue; + } + + if (response_hex != NULL) { + const char *response_contains = jsonStrGet(data, "ResponseContains"); + if (response_contains && ResponseContainsMatch(response_contains, response_hex)) { + contains_elm = data; } } } - if (elm == NULL) { - goto out; + json_t *elm = contains_elm ? contains_elm : fallback_elm; + if (elm != NULL) { + const char *vaid = jsonStrGet(elm, "AID"); + const char *vendor = jsonStrGet(elm, "Vendor"); + const char *name = jsonStrGet(elm, "Name"); + const char *country = jsonStrGet(elm, "Country"); + const char *description = jsonStrGet(elm, "Description"); + const char *type = jsonStrGet(elm, "Type"); + + if (verbose == false) { + PrintAndLogEx(SUCCESS, "AID : " _YELLOW_("%s") " | %s | %s", vaid, vendor, name); + } else { + PrintAndLogEx(SUCCESS, "Input AID..... " _YELLOW_("%s"), aid); + if (aid) + PrintAndLogEx(SUCCESS, "Found AID..... " _YELLOW_("%s"), vaid); + if (vendor) + PrintAndLogEx(SUCCESS, "Vendor........ " _YELLOW_("%s"), vendor); + if (type) + PrintAndLogEx(SUCCESS, "Type.......... " _YELLOW_("%s"), type); + if (name) + PrintAndLogEx(SUCCESS, "Name.......... " _YELLOW_("%s"), name); + if (country) + PrintAndLogEx(SUCCESS, "Country....... %s", country); + if (description) + PrintAndLogEx(SUCCESS, "Description... %s", description); + } } - // print here - const char *vaid = jsonStrGet(elm, "AID"); - const char *vendor = jsonStrGet(elm, "Vendor"); - const char *name = jsonStrGet(elm, "Name"); - const char *country = jsonStrGet(elm, "Country"); - const char *description = jsonStrGet(elm, "Description"); - const char *type = jsonStrGet(elm, "Type"); - - if (verbose == false) { - PrintAndLogEx(SUCCESS, "AID : " _YELLOW_("%s") " | %s | %s", vaid, vendor, name); - } else { - PrintAndLogEx(SUCCESS, "Input AID..... " _YELLOW_("%s"), aid); - if (aid) - PrintAndLogEx(SUCCESS, "Found AID..... " _YELLOW_("%s"), vaid); - if (vendor) - PrintAndLogEx(SUCCESS, "Vendor........ " _YELLOW_("%s"), vendor); - if (type) - PrintAndLogEx(SUCCESS, "Type.......... " _YELLOW_("%s"), type); - if (name) - PrintAndLogEx(SUCCESS, "Name.......... " _YELLOW_("%s"), name); - if (country) - PrintAndLogEx(SUCCESS, "Country....... %s", country); - if (description) - PrintAndLogEx(SUCCESS, "Description... %s", description); + if (response_hex != NULL) { + free(response_hex); } out: @@ -187,8 +290,3 @@ out: } return retval; } - -int PrintAIDDescriptionBuf(json_t *root, uint8_t *aid, size_t aidlen, bool verbose) { - return PrintAIDDescription(root, sprint_hex_inrow(aid, aidlen), verbose); -} - diff --git a/client/src/aidsearch.h b/client/src/aidsearch.h index b394d43df..ab09bbbf7 100644 --- a/client/src/aidsearch.h +++ b/client/src/aidsearch.h @@ -26,10 +26,12 @@ #include "jansson.h" int PrintAIDDescription(json_t *xroot, char *aid, bool verbose); +int PrintAIDDescriptionEx(json_t *xroot, char *aid, const uint8_t *response, size_t response_len, bool verbose); int PrintAIDDescriptionBuf(json_t *root, uint8_t *aid, size_t aidlen, bool verbose); json_t *AIDSearchInit(bool verbose); json_t *AIDSearchGetElm(json_t *root, size_t elmindx); bool AIDGetFromElm(json_t *data, uint8_t *aid, size_t aidmaxlen, int *aidlen); +bool AIDSeenBefore(json_t *root, const uint8_t *aid, size_t aidlen, size_t before_index); int AIDSearchFree(json_t *root); #endif diff --git a/client/src/cmdanalyse.c b/client/src/cmdanalyse.c index 9c2d7ec8b..74873b94c 100644 --- a/client/src/cmdanalyse.c +++ b/client/src/cmdanalyse.c @@ -36,6 +36,7 @@ #include "cliparser.h" #include "generator.h" // generate nuid #include "iso14b.h" // defines for ETU conversions +#include "util.h" // regex utility static int CmdHelp(const char *Cmd); @@ -1168,6 +1169,91 @@ static int CmdAnalyseUnits(const char *Cmd) { return PM3_SUCCESS; } +static int CmdAnalyseRegex(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "analyse regex", + "Regex utility (subset: ^ $ . * with \\\\ escape)", + "analyse regex --pattern '^A000' --text A000000476D0000111\n" + "analyse regex --pattern '.*500A416E64726F6964506179.*9000$' --text 6F8150500A416E64726F69645061799000 --insensitive\n" + "analyse regex --test" + ); + + void *argtable[] = { + arg_param_begin, + arg_str0("p", "pattern", "", "regex pattern"), + arg_str0("d", "text", "", "text to match"), + arg_lit0("i", "insensitive", "case-insensitive match"), + arg_lit0("t", "test", "run self tests"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + struct arg_str *arg_pattern = arg_get_str(ctx, 1); + struct arg_str *arg_text = arg_get_str(ctx, 2); + bool insensitive = arg_get_lit(ctx, 3); + bool selftest = arg_get_lit(ctx, 4); + + if (selftest) { + CLIParserFree(ctx); + + typedef struct { + const char *pattern; + const char *text; + bool case_insensitive; + bool expect_match; + } regex_test_case_t; + + const regex_test_case_t tests[] = { + {.pattern = "^A000", .text = "A000000476D0000111", .case_insensitive = false, .expect_match = true}, + {.pattern = "9000$", .text = "6F009000", .case_insensitive = false, .expect_match = true}, + {.pattern = ".*500A416E64726F6964506179.*9000$", .text = "6F8150500A416E64726F69645061799000", .case_insensitive = true, .expect_match = true}, + {.pattern = "^a0.*$", .text = "A0000000", .case_insensitive = true, .expect_match = true}, + {.pattern = "^a0.*$", .text = "B0000000", .case_insensitive = true, .expect_match = false}, + {.pattern = "A\\*B", .text = "ZZA*BZZ", .case_insensitive = false, .expect_match = true}, + {.pattern = "A+B", .text = "AAAB", .case_insensitive = false, .expect_match = false}, + {.pattern = "*ABC", .text = "ABC", .case_insensitive = false, .expect_match = false}, + {.pattern = "ABC\\", .text = "ABC", .case_insensitive = false, .expect_match = false}, + }; + + bool all_ok = true; + for (size_t i = 0; i < ARRAYLEN(tests); i++) { + bool matched = tests[i].case_insensitive + ? str_regex_match_case_insensitive(tests[i].pattern, tests[i].text) + : str_regex_match(tests[i].pattern, tests[i].text); + + bool ok = (matched == tests[i].expect_match); + PrintAndLogEx(ok ? SUCCESS : FAILED, "%zu. pattern=`%s` valid=%s match=%s ( %s )", + i + 1, + tests[i].pattern, + "true", + matched ? "true" : "false", + ok ? _GREEN_("ok") : _RED_("fail")); + if (!ok) { + all_ok = false; + } + } + + PrintAndLogEx(all_ok ? SUCCESS : FAILED, "Tests ( %s )", all_ok ? _GREEN_("ok") : _RED_("fail")); + return all_ok ? PM3_SUCCESS : PM3_ESOFT; + } + + if (arg_pattern->count == 0 || arg_text->count == 0) { + CLIParserFree(ctx); + PrintAndLogEx(ERR, "pattern and text are required unless --test is used"); + return PM3_EINVARG; + } + + const char *pattern = arg_pattern->sval[0]; + const char *text = arg_text->sval[0]; + + bool matched = insensitive + ? str_regex_match_case_insensitive(pattern, text) + : str_regex_match(pattern, text); + CLIParserFree(ctx); + PrintAndLogEx(matched ? SUCCESS : INFO, "Regex match: %s", matched ? _GREEN_("true") : _YELLOW_("false")); + return PM3_SUCCESS; +} + static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help"}, {"lrc", CmdAnalyseLRC, AlwaysAvailable, "Generate final byte for XOR LRC"}, @@ -1180,6 +1266,7 @@ static command_t CommandTable[] = { {"demodbuff", CmdAnalyseDemodBuffer, AlwaysAvailable, "Load binary string to DemodBuffer"}, {"freq", CmdAnalyseFreq, AlwaysAvailable, "Calc wave lengths"}, {"foo", CmdAnalyseFoo, AlwaysAvailable, "muxer"}, + {"regex", CmdAnalyseRegex, AlwaysAvailable, "Regex utility (subset: ^ $ . * with \\\\ escape)"}, {"units", CmdAnalyseUnits, AlwaysAvailable, "convert ETU <> US <> SSP_CLK (3.39MHz)"}, {NULL, NULL, NULL, NULL} }; diff --git a/client/src/cmddata.c b/client/src/cmddata.c index 94bf3662f..bb6c3e9fd 100644 --- a/client/src/cmddata.c +++ b/client/src/cmddata.c @@ -3982,24 +3982,71 @@ static int CmdQRcode(const char *Cmd) { CLIParserInit(&ctx, "data qrcode", "Generate a QR code with the input data", "data qrcode -f \n" - "data qrcode -d 123456789\n" + "data qrcode -d 0123456789\n" + "data qrcode -d AABBCCDD --ascii\n" ); void *argtable[] = { arg_param_begin, + arg_lit0("a", "ascii", "Render with ASCII-safe characters"), arg_str0("f", "file", "", "Specify a filename"), - arg_str0("d", "data", "", "message as hex bytes"), + arg_str0("d", "data", "", "message as a single hex byte string"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); + bool ascii = arg_get_lit(ctx, 1); int fnlen = 0; char filename[FILE_PATH_SIZE] = { 0 }; - CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); + CLIParamStrToBuf(arg_get_str(ctx, 2), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); int dlen = 0; - char data[1024] = { 0 }; - CLIParamStrToBuf(arg_get_str(ctx, 2), (uint8_t *)data, sizeof(data), &dlen); + uint8_t data[512] = { 0 }; + int data_arg_len = 0; + char data_arg[1024] = { 0 }; + struct arg_str *data_arg_str = arg_get_str(ctx, 3); + int data_count = data_arg_str->count; + + if (data_count > 1) { + CLIParserFree(ctx); + PrintAndLogEx(ERR, "QR data accepts exactly one -d value. Use a single contiguous hex string and encode spaces as 20."); + return PM3_EINVARG; + } + + if (data_count == 1) { + if (CLIParamStrToBuf(data_arg_str, (uint8_t *)data_arg, sizeof(data_arg), &data_arg_len) != 0) { + CLIParserFree(ctx); + return PM3_EINVARG; + } + + if (strpbrk(data_arg, " \t") != NULL) { + CLIParserFree(ctx); + PrintAndLogEx(ERR, "QR data must be one contiguous hex string. Spaces are not supported; encode a space byte as 20."); + return PM3_EINVARG; + } + + for (int i = 0; i < data_arg_len; i++) { + if (isxdigit((unsigned char)data_arg[i]) == 0) { + CLIParserFree(ctx); + PrintAndLogEx(ERR, "QR data must contain only hex characters 0-9, a-f, or A-F."); + return PM3_EINVARG; + } + } + + if (data_arg_len & 1) { + CLIParserFree(ctx); + PrintAndLogEx(ERR, "QR data must contain an even number of hex digits."); + return PM3_EINVARG; + } + + dlen = hex_to_bytes(data_arg, data, sizeof(data)); + if (dlen < 0) { + CLIParserFree(ctx); + PrintAndLogEx(ERR, "QR data must be valid hex bytes."); + return PM3_EINVARG; + } + } + CLIParserFree(ctx); if (fnlen && dlen) { @@ -4028,17 +4075,27 @@ static int CmdQRcode(const char *Cmd) { } if (dlen) { + int8_t smallest_version = qrcode_getMinVersionForBytes(dlen, ECC_LOW); + if (smallest_version < 1) { + PrintAndLogEx(ERR, "QR data is too large to fit in a supported QR code."); + return PM3_EINVARG; + } - // calc size of input data to get the correct - int smallest_version = (dlen + 17) / 20; uint8_t qr_arr[qrcode_getBufferSize(smallest_version)]; - qrcode_initText(&qrcode, qr_arr, smallest_version, ECC_LOW, (char *) data); + if (qrcode_initBytes(&qrcode, qr_arr, smallest_version, ECC_LOW, data, dlen) < 0) { + PrintAndLogEx(ERR, "Failed to encode QR data."); + return PM3_ESOFT; + } PrintAndLogEx(NORMAL, ""); - qrcode_print_matrix_utf8(&qrcode); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, ""); - qrcode_print_matrix_utf8_2x2(&qrcode); + if (ascii) { + qrcode_print_matrix_ascii(&qrcode); + } else { + qrcode_print_matrix_utf8(&qrcode); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, ""); + qrcode_print_matrix_utf8_2x2(&qrcode); + } PrintAndLogEx(NORMAL, ""); } diff --git a/client/src/cmdhf14a.c b/client/src/cmdhf14a.c index 122c182a1..44ef40c3a 100644 --- a/client/src/cmdhf14a.c +++ b/client/src/cmdhf14a.c @@ -618,7 +618,7 @@ static const char *get_uid_type(iso14a_card_select_t *card) { int Hf14443_4aGetCardData(iso14a_card_select_t *card) { - SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT, 0, 0, NULL, 0); + SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_CLEARTRACE, 0, 0, NULL, 0); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) { PrintAndLogEx(WARNING, "timeout while waiting for reply"); @@ -695,9 +695,10 @@ static int CmdHF14AReader(const char *Cmd) { bool silent = arg_get_lit(ctx, 2); - uint32_t cm = ISO14A_CONNECT; + uint32_t cm = ISO14A_CONNECT | ISO14A_CLEARTRACE; if (arg_get_lit(ctx, 3)) { cm &= ~ISO14A_CONNECT; + cm &= ~ISO14A_CLEARTRACE; } if (arg_get_lit(ctx, 4)) { @@ -872,7 +873,7 @@ static int CmdHF14ACUIDs(const char *Cmd) { } // execute anticollision procedure - SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_NO_RATS, 0, 0, NULL, 0); + SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_CLEARTRACE | ISO14A_NO_RATS, 0, 0, NULL, 0); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) { @@ -1225,9 +1226,9 @@ int SelectCard14443A_4_WithParameters(bool disconnect, bool verbose, iso14a_card // Anticollision + SELECT card PacketResponseNG resp; if (polling_parameters != NULL) { - SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_NO_DISCONNECT | ISO14A_USE_CUSTOM_POLLING, 0, 0, (uint8_t *)polling_parameters, sizeof(iso14a_polling_parameters_t)); + SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_CLEARTRACE | ISO14A_NO_DISCONNECT | ISO14A_USE_CUSTOM_POLLING, 0, 0, (uint8_t *)polling_parameters, sizeof(iso14a_polling_parameters_t)); } else { - SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_NO_DISCONNECT, 0, 0, NULL, 0); + SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_CLEARTRACE | ISO14A_NO_DISCONNECT, 0, 0, NULL, 0); } if (WaitForResponseTimeout(CMD_ACK, &resp, 2000) == false) { @@ -1719,6 +1720,7 @@ static int CmdHF14ACmdRaw(const char *Cmd) { if (active || active_select) { flags |= ISO14A_CONNECT; + flags |= ISO14A_CLEARTRACE; if (active) flags |= ISO14A_NO_SELECT; } @@ -2695,7 +2697,7 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { } clearCommandBuffer(); - SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_NO_DISCONNECT, 0, 0, NULL, 0); + SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_CLEARTRACE | ISO14A_NO_DISCONNECT, 0, 0, NULL, 0); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) { PrintAndLogEx(DEBUG, "iso14443a card select timeout"); @@ -2912,7 +2914,7 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { // reconnect for further tests clearCommandBuffer(); - SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_NO_DISCONNECT, 0, 0, NULL, 0); + SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_CLEARTRACE | ISO14A_NO_DISCONNECT, 0, 0, NULL, 0); if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) { PrintAndLogEx(WARNING, "timeout while waiting for reply"); DropField(); @@ -3182,6 +3184,9 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { if ((AIDGetFromElm(data, vaid, sizeof(vaid), &vaidlen) == false) || (vaidlen == 0)) { continue; } + if (AIDSeenBefore(root, vaid, (size_t)vaidlen, elmindx)) { + continue; + } uint16_t sw = 0; uint8_t result[1024] = {0}; @@ -3214,7 +3219,15 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { if (verbose) PrintAndLogEx(WARNING, "Application ( " _RED_("blocked") " )"); } - PrintAIDDescriptionBuf(root, vaid, vaidlen, verbose); + uint8_t response_for_match[1026] = {0}; + size_t response_for_match_len = resultlen; + memcpy(response_for_match, result, MIN(resultlen, sizeof(response_for_match))); + if ((response_for_match_len + 2) <= sizeof(response_for_match)) { + response_for_match[response_for_match_len] = sw >> 8; + response_for_match[response_for_match_len + 1] = sw & 0xff; + response_for_match_len += 2; + } + PrintAIDDescriptionEx(root, sprint_hex_inrow(vaid, vaidlen), response_for_match, response_for_match_len, verbose); if (dfnamelen) { if (dfnamelen == vaidlen) { diff --git a/client/src/cmdhf14b.c b/client/src/cmdhf14b.c index b605f5c7d..46ef875f6 100644 --- a/client/src/cmdhf14b.c +++ b/client/src/cmdhf14b.c @@ -122,6 +122,9 @@ static void hf14b_aid_search(bool verbose) { if ((AIDGetFromElm(data, vaid, sizeof(vaid), &vaidlen) == false) || (vaidlen == 0)) { continue; } + if (AIDSeenBefore(root, vaid, (size_t)vaidlen, elmindx)) { + continue; + } // COMPUTE APDU @@ -172,7 +175,7 @@ static void hf14b_aid_search(bool verbose) { } } - PrintAIDDescriptionBuf(root, vaid, vaidlen, verbose); + PrintAIDDescriptionEx(root, sprint_hex_inrow(vaid, vaidlen), result, (size_t)resultlen, verbose); if (dfnamelen) { if (dfnamelen == vaidlen) { diff --git a/client/src/cmdhffudan.c b/client/src/cmdhffudan.c index 817749353..18cd0b258 100644 --- a/client/src/cmdhffudan.c +++ b/client/src/cmdhffudan.c @@ -111,7 +111,7 @@ static int fudan_get_type(iso14a_card_select_t *card, bool verbose) { } clearCommandBuffer(); - SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_NO_DISCONNECT, 0, 0, NULL, 0); + SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_CLEARTRACE | ISO14A_NO_DISCONNECT, 0, 0, NULL, 0); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) { PrintAndLogEx(DEBUG, "iso14443a card select timeout"); diff --git a/client/src/cmdhfgallagher.c b/client/src/cmdhfgallagher.c index 055a310f6..18dd412bc 100644 --- a/client/src/cmdhfgallagher.c +++ b/client/src/cmdhfgallagher.c @@ -1048,7 +1048,7 @@ static int hfgal_write_classic_card(GallagherCredentials_t *creds, uint8_t cred_ clearCommandBuffer(); // Select card to get UID - SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT, 0, 0, NULL, 0); + SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_CLEARTRACE, 0, 0, NULL, 0); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) { PrintAndLogEx(ERR, "Card select timeout"); @@ -1177,7 +1177,7 @@ static int hfgal_read_classic_card(uint8_t *site_key, bool verbose, bool quiet) GallagherCredentials_t creds = {0}; // Select card - SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT, 0, 0, NULL, 0); + SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_CLEARTRACE, 0, 0, NULL, 0); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) { if (!quiet) { diff --git a/client/src/cmdhfict.c b/client/src/cmdhfict.c index 5a7e6fadd..7c749d5d2 100644 --- a/client/src/cmdhfict.c +++ b/client/src/cmdhfict.c @@ -403,7 +403,7 @@ static int ict_select_card(iso14a_card_select_t *card) { } clearCommandBuffer(); - SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT, 0, 0, NULL, 0); + SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_CLEARTRACE, 0, 0, NULL, 0); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) { return PM3_ESOFT; diff --git a/client/src/cmdhflto.c b/client/src/cmdhflto.c index 1eadb3d67..fc4e925bf 100644 --- a/client/src/cmdhflto.c +++ b/client/src/cmdhflto.c @@ -107,7 +107,7 @@ static void lto_switch_off_field(void) { } static void lto_switch_on_field(void) { - SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_NO_SELECT | ISO14A_NO_DISCONNECT | ISO14A_NO_RATS, 0, 0, NULL, 0); + SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_CLEARTRACE | ISO14A_NO_SELECT | ISO14A_NO_DISCONNECT | ISO14A_NO_RATS, 0, 0, NULL, 0); } // send a raw LTO-CM command, returns the length of the response (0 in case of error) diff --git a/client/src/cmdhfmf.c b/client/src/cmdhfmf.c index 547064b0c..b3db4e784 100644 --- a/client/src/cmdhfmf.c +++ b/client/src/cmdhfmf.c @@ -369,7 +369,7 @@ int mfc_ev1_print_signature(uint8_t *uid, uint8_t uidlen, uint8_t *signature, in int mf_read_uid(uint8_t *uid, int *uidlen, int *nxptype) { clearCommandBuffer(); - SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_NO_DISCONNECT, 0, 0, NULL, 0); + SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_CLEARTRACE | ISO14A_NO_DISCONNECT, 0, 0, NULL, 0); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) { PrintAndLogEx(DEBUG, "iso14443a card select failed"); @@ -897,7 +897,7 @@ static int mfc_read_tag(iso14a_card_select_t *card, uint8_t *carddata, uint8_t n // Select card to get UID/UIDLEN/ATQA/SAK information clearCommandBuffer(); - SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT, 0, 0, NULL, 0); + SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_CLEARTRACE, 0, 0, NULL, 0); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) { PrintAndLogEx(DEBUG, "iso14443a card select timeout"); @@ -3059,7 +3059,7 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { // Select card to get UID/UIDLEN/ATQA/SAK information clearCommandBuffer(); - SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_NO_DISCONNECT, 0, 0, NULL, 0); + SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_CLEARTRACE | ISO14A_NO_DISCONNECT, 0, 0, NULL, 0); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) { PrintAndLogEx(DEBUG, "iso14443a card select timeout"); @@ -6287,7 +6287,7 @@ static int CmdHF14AMfCSave(const char *Cmd) { // Select card to get UID/UIDLEN information clearCommandBuffer(); - SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT, 0, 0, NULL, 0); + SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_CLEARTRACE, 0, 0, NULL, 0); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) { PrintAndLogEx(DEBUG, "iso14443a card select timeout"); @@ -6437,7 +6437,7 @@ static int CmdHF14AMfCView(const char *Cmd) { // Select card to get UID/UIDLEN information clearCommandBuffer(); - SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT, 0, 0, NULL, 0); + SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_CLEARTRACE, 0, 0, NULL, 0); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) { PrintAndLogEx(DEBUG, "iso14443a card select timeout"); @@ -7289,7 +7289,7 @@ int CmdHFMFNDEFFormat(const char *Cmd) { // Select card to get UID/UIDLEN/ATQA/SAK information clearCommandBuffer(); - SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT, 0, 0, NULL, 0); + SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_CLEARTRACE, 0, 0, NULL, 0); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) { PrintAndLogEx(DEBUG, "iso14443a card select timeout"); @@ -8023,7 +8023,7 @@ static int CmdHf14AMfSuperCard(const char *Cmd) { for (i = 0; i < FURUI_MAX_TRACES; i++) { uint8_t data[] = {0xAA, 0xA8, 0x00, i}; - uint32_t flags = ISO14A_CONNECT | ISO14A_RAW | ISO14A_APPEND_CRC | ISO14A_NO_RATS; + uint32_t flags = ISO14A_CONNECT | ISO14A_CLEARTRACE | ISO14A_RAW | ISO14A_APPEND_CRC | ISO14A_NO_RATS; clearCommandBuffer(); SendCommandMIX(CMD_HF_ISO14443A_READER, flags, sizeof(data), 0, data, sizeof(data)); if (WaitForResponseTimeout(CMD_ACK, NULL, 1500) == false) { @@ -8054,7 +8054,7 @@ static int CmdHf14AMfSuperCard(const char *Cmd) { for (i = 0; i < SUPER_MAX_TRACES; i++) { uint8_t data[] = {0x30, i}; - uint32_t flags = ISO14A_CONNECT | ISO14A_RAW | ISO14A_APPEND_CRC | ISO14A_NO_RATS; + uint32_t flags = ISO14A_CONNECT | ISO14A_CLEARTRACE | ISO14A_RAW | ISO14A_APPEND_CRC | ISO14A_NO_RATS; clearCommandBuffer(); SendCommandMIX(CMD_HF_ISO14443A_READER, flags, sizeof(data), 0, data, sizeof(data)); if (WaitForResponseTimeout(CMD_ACK, NULL, 1500) == false) { @@ -9271,7 +9271,7 @@ static int CmdHF14AGen4Save(const char *Cmd) { // Select card to get UID/UIDLEN information clearCommandBuffer(); - SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT, 0, 0, NULL, 0); + SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_CLEARTRACE, 0, 0, NULL, 0); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) { PrintAndLogEx(DEBUG, "iso14443a card select timeout"); @@ -10268,7 +10268,7 @@ static int CmdHF14AMfInfo(const char *Cmd) { } clearCommandBuffer(); - SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_NO_DISCONNECT, 0, 0, NULL, 0); + SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_CLEARTRACE | ISO14A_NO_DISCONNECT, 0, 0, NULL, 0); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) { PrintAndLogEx(DEBUG, "iso14443a card select timeout"); @@ -10830,7 +10830,7 @@ static int CmdHF14AMfISEN(const char *Cmd) { } clearCommandBuffer(); - SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT, 0, 0, NULL, 0); + SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_CLEARTRACE, 0, 0, NULL, 0); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) { PrintAndLogEx(DEBUG, "iso14443a card select timeout"); diff --git a/client/src/cmdhfmfdes.c b/client/src/cmdhfmfdes.c index 276e6b241..5f3bb0ece 100644 --- a/client/src/cmdhfmfdes.c +++ b/client/src/cmdhfmfdes.c @@ -1514,7 +1514,7 @@ static int CmdHF14aDesChk(const char *Cmd) { if ((jsonnamelen > 0) && result) { DropField(); // MIFARE DESFire info - SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT, 0, 0, NULL, 0); + SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_CLEARTRACE, 0, 0, NULL, 0); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) { PrintAndLogEx(WARNING, "timeout while waiting for reply"); diff --git a/client/src/cmdhfmfp.c b/client/src/cmdhfmfp.c index 3dc3f5078..23a7edc92 100644 --- a/client/src/cmdhfmfp.c +++ b/client/src/cmdhfmfp.c @@ -221,7 +221,7 @@ static int mfp_read_card_id(iso14a_card_select_t *card, int *nxptype) { } clearCommandBuffer(); - SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_NO_DISCONNECT, 0, 0, NULL, 0); + SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_CLEARTRACE | ISO14A_NO_DISCONNECT, 0, 0, NULL, 0); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) { PrintAndLogEx(DEBUG, "iso14443a card select failed"); @@ -383,7 +383,7 @@ static int CmdHFMFPInfo(const char *Cmd) { PrintAndLogEx(INFO, "--- " _CYAN_("Tag Information") " ---------------------------"); // Mifare Plus info - SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT, 0, 0, NULL, 0); + SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_CLEARTRACE, 0, 0, NULL, 0); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_ACK, &resp, 2000) == false) { PrintAndLogEx(DEBUG, "iso14443a card select timeout"); diff --git a/client/src/cmdhfmfu.c b/client/src/cmdhfmfu.c index 61fd6c06e..c07dca3c6 100644 --- a/client/src/cmdhfmfu.c +++ b/client/src/cmdhfmfu.c @@ -329,7 +329,7 @@ int ul_read_uid(uint8_t *uid) { } // read uid from tag clearCommandBuffer(); - SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_NO_RATS, 0, 0, NULL, 0); + SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_CLEARTRACE | ISO14A_NO_RATS, 0, 0, NULL, 0); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) { PrintAndLogEx(WARNING, "timeout while waiting for reply"); @@ -358,7 +358,7 @@ int ul_read_uid(uint8_t *uid) { static void ul_switch_on_field(void) { clearCommandBuffer(); - SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_NO_DISCONNECT | ISO14A_NO_RATS, 0, 0, NULL, 0); + SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_CLEARTRACE | ISO14A_NO_DISCONNECT | ISO14A_NO_RATS, 0, 0, NULL, 0); } static int ul_send_cmd_raw(const uint8_t *cmd, uint8_t cmdlen, uint8_t *response, uint16_t responseLength, bool schann) { @@ -487,7 +487,7 @@ static int ulc_requestAuthentication(uint8_t *nonce, uint16_t nonceLength) { } int mfuc_test_authentication_support(void) { - SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_NO_DISCONNECT, 0, 0, NULL, 0); + SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_CLEARTRACE | ISO14A_NO_DISCONNECT, 0, 0, NULL, 0); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) { PrintAndLogEx(DEBUG, "iso14443a card select timeout"); @@ -2004,7 +2004,7 @@ static int mfulc_fingerprint(void) { // GT23SC4489 uint8_t cmd3a[] = {0x26}; uint8_t cmd3b[] = {0x30}; - SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_RAW | ISO14A_CONNECT | ISO14A_NO_SELECT | ISO14A_NO_DISCONNECT, 7 << 16, 0, cmd3a, sizeof(cmd3a)); + SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_RAW | ISO14A_CONNECT | ISO14A_CLEARTRACE | ISO14A_NO_SELECT | ISO14A_NO_DISCONNECT, 7 << 16, 0, cmd3a, sizeof(cmd3a)); if (WaitForResponseTimeout(CMD_ACK, &resp, 500)) { if (resp.oldarg[0] == 2) { SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_RAW | ISO14A_NO_SELECT, sizeof(cmd3b), 0, cmd3b, sizeof(cmd3b)); @@ -5187,7 +5187,7 @@ static int CmdHF14AMfUKeyGen(const char *Cmd) { if (read_tag) { // read uid from tag clearCommandBuffer(); - SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_NO_RATS, 0, 0, NULL, 0); + SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_CLEARTRACE | ISO14A_NO_RATS, 0, 0, NULL, 0); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) { PrintAndLogEx(WARNING, "timeout while waiting for reply"); diff --git a/client/src/cmdhfsaflok.c b/client/src/cmdhfsaflok.c index 99d54bb87..6eb9d13b5 100644 --- a/client/src/cmdhfsaflok.c +++ b/client/src/cmdhfsaflok.c @@ -1128,7 +1128,7 @@ static void saflok_encode( // perhaps by creating a struct to avoid having to pass a length parameter. static int saflok_read_sector(int sector, uint8_t *secdata) { clearCommandBuffer(); - SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT, 0, 0, NULL, 0); + SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_CLEARTRACE, 0, 0, NULL, 0); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) { PrintAndLogEx(DEBUG, "iso14443a card select failed"); diff --git a/client/src/cmdhfst25ta.c b/client/src/cmdhfst25ta.c index b2846b689..76c32d154 100644 --- a/client/src/cmdhfst25ta.c +++ b/client/src/cmdhfst25ta.c @@ -42,7 +42,7 @@ static int CmdHelp(const char *Cmd); static bool st25ta_select(iso14a_card_select_t *card) { clearCommandBuffer(); - SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_NO_DISCONNECT | ISO14A_NO_RATS, 0, 0, NULL, 0); + SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_CLEARTRACE | ISO14A_NO_DISCONNECT | ISO14A_NO_RATS, 0, 0, NULL, 0); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) { PrintAndLogEx(DEBUG, "iso14443a card select timeout"); diff --git a/client/src/cmdhftopaz.c b/client/src/cmdhftopaz.c index ca3efdaa3..f8d1d6324 100644 --- a/client/src/cmdhftopaz.c +++ b/client/src/cmdhftopaz.c @@ -41,7 +41,7 @@ static topaz_tag_t topaz_tag; static void topaz_switch_on_field(void) { - SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_NO_SELECT | ISO14A_NO_DISCONNECT | ISO14A_TOPAZMODE | ISO14A_NO_RATS, 0, 0, NULL, 0); + SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_CLEARTRACE | ISO14A_NO_SELECT | ISO14A_NO_DISCONNECT | ISO14A_TOPAZMODE | ISO14A_NO_RATS, 0, 0, NULL, 0); } static void topaz_switch_off_field(void) { diff --git a/client/src/cmdhfwaveshare.c b/client/src/cmdhfwaveshare.c index c4ede2b3c..a22202ada 100644 --- a/client/src/cmdhfwaveshare.c +++ b/client/src/cmdhfwaveshare.c @@ -249,7 +249,7 @@ static int start_drawing(uint8_t model_nr, uint8_t *black, uint8_t *red) { uint16_t actrxlen[20]; clearCommandBuffer(); - SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_NO_DISCONNECT, 0, 0, NULL, 0); + SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_CLEARTRACE | ISO14A_NO_DISCONNECT, 0, 0, NULL, 0); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) { PrintAndLogEx(ERR, "No tag found"); diff --git a/client/src/cmdsmartcard.c b/client/src/cmdsmartcard.c index 5d01f9a9d..8d3586732 100644 --- a/client/src/cmdsmartcard.c +++ b/client/src/cmdsmartcard.c @@ -1312,7 +1312,9 @@ static int CmdPCSC(const char *Cmd) { } } - uint8_t res[22] = {0}; + // ISO 7816-3 specifies that ATRs can be 2 to 33 bytes long + // but some custom cards may support up to 256 bytes long ATRs + uint8_t res[2 + 256] = {0}; res[1] = atrLen; memcpy(res + 2, atr, atrLen); mbedtls_net_send(&netCtx, res, 2 + atrLen); @@ -1393,7 +1395,7 @@ static int CmdPCSC(const char *Cmd) { // ISO 15. - if (use_contact && IfPm3Iso14443() && smart_select(false, &card) == PM3_SUCCESS) { + if (use_contact && IfPm3Iso14443() && smart_select(false, &card)) { have_card = true; card_type = CC_CONTACT; } diff --git a/client/src/mifare/desfirecore.c b/client/src/mifare/desfirecore.c index c8993257e..500c9237c 100644 --- a/client/src/mifare/desfirecore.c +++ b/client/src/mifare/desfirecore.c @@ -3222,7 +3222,7 @@ int DesfireISOAppendRecord(DesfireContext_t *dctx, uint8_t fileid, uint8_t *data int DesfireGetCardUID(DesfireContext_t *ctx) { iso14a_card_select_t card = {0}; - SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT, 0, 0, NULL, 0); + SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_CLEARTRACE, 0, 0, NULL, 0); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) { PrintAndLogEx(WARNING, "timeout while waiting for reply"); diff --git a/client/src/mifare/gen4.c b/client/src/mifare/gen4.c index 7487d127d..9a9fa3e17 100644 --- a/client/src/mifare/gen4.c +++ b/client/src/mifare/gen4.c @@ -60,7 +60,7 @@ static int mfG4ExCommand(uint8_t cmd, uint8_t *pwd, uint8_t *data, size_t datale int resplen = 0; clearCommandBuffer(); - SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_RAW | ISO14A_NO_RATS | ISO14A_APPEND_CRC, 6 + datalen, 0, (uint8_t *)&payload, 6 + datalen); + SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_CLEARTRACE | ISO14A_RAW | ISO14A_NO_RATS | ISO14A_APPEND_CRC, 6 + datalen, 0, (uint8_t *)&payload, 6 + datalen); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { diff --git a/client/src/mifare/mifarehost.c b/client/src/mifare/mifarehost.c index c88b60f32..24489f8c1 100644 --- a/client/src/mifare/mifarehost.c +++ b/client/src/mifare/mifarehost.c @@ -1392,7 +1392,7 @@ int detect_classic_prng(void) { PacketResponseNG resp, respA; uint8_t cmd[] = {MIFARE_AUTH_KEYA, 0x00}; - uint32_t flags = ISO14A_CONNECT | ISO14A_RAW | ISO14A_APPEND_CRC | ISO14A_NO_RATS; + uint32_t flags = ISO14A_CONNECT | ISO14A_CLEARTRACE | ISO14A_RAW | ISO14A_APPEND_CRC | ISO14A_NO_RATS; clearCommandBuffer(); SendCommandMIX(CMD_HF_ISO14443A_READER, flags, sizeof(cmd), 0, cmd, sizeof(cmd)); @@ -1432,7 +1432,7 @@ int detect_classic_auth(uint8_t key_type) { PacketResponseNG resp, respA; uint8_t cmd[] = {MIFARE_AUTH_KEYA + key_type, 0x00}; - uint32_t flags = ISO14A_CONNECT | ISO14A_RAW | ISO14A_APPEND_CRC | ISO14A_NO_RATS; + uint32_t flags = ISO14A_CONNECT | ISO14A_CLEARTRACE | ISO14A_RAW | ISO14A_APPEND_CRC | ISO14A_NO_RATS; clearCommandBuffer(); SendCommandMIX(CMD_HF_ISO14443A_READER, flags, sizeof(cmd), 0, cmd, sizeof(cmd)); diff --git a/client/src/qrcode/qrcode.c b/client/src/qrcode/qrcode.c index cb04c3f05..efe87c0ed 100644 --- a/client/src/qrcode/qrcode.c +++ b/client/src/qrcode/qrcode.c @@ -793,6 +793,34 @@ uint16_t qrcode_getBufferSize(uint8_t version) { return bb_getGridSizeBytes(4 * version + 17); } +int8_t qrcode_getMinVersionForBytes(uint16_t length, uint8_t ecc) { + if (ecc > ECC_HIGH) { + return -1; + } + + uint8_t eccFormatBits = (ECC_FORMAT_BITS >> (2 * ecc)) & 0x03; + +#if LOCK_VERSION == 0 + for (uint8_t version = 1; version <= 40; version++) { + uint16_t moduleCount = NUM_RAW_DATA_MODULES[version - 1]; + uint16_t dataCapacity = moduleCount / 8 - NUM_ERROR_CORRECTION_CODEWORDS[eccFormatBits][version - 1]; + uint32_t requiredBits = 4 + getModeBits(version, MODE_BYTE) + ((uint32_t)length * 8); + + if (requiredBits <= ((uint32_t)dataCapacity * 8)) { + return version; + } + } + + return -1; +#else + uint8_t version = LOCK_VERSION; + uint16_t moduleCount = NUM_RAW_DATA_MODULES; + uint16_t dataCapacity = moduleCount / 8 - NUM_ERROR_CORRECTION_CODEWORDS[eccFormatBits]; + uint32_t requiredBits = 4 + getModeBits(version, MODE_BYTE) + ((uint32_t)length * 8); + return (requiredBits <= ((uint32_t)dataCapacity * 8)) ? version : -1; +#endif +} + // @TODO: Return error if data is too big. int8_t qrcode_initBytes(QRCode *qrcode, uint8_t *modules, uint8_t version, uint8_t ecc, uint8_t *data, uint16_t length) { uint8_t size = version * 4 + 17; @@ -885,6 +913,15 @@ bool qrcode_getModule(QRCode *qrcode, uint8_t x, uint8_t y) { return (qrcode->modules[offset >> 3] & (1 << (7 - (offset & 0x07)))) != 0; } +void qrcode_print_matrix_ascii(QRCode *q) { + for (uint8_t y = 0; y < q->size; y++) { + for (uint8_t x = 0; x < q->size; x++) { + PrintAndLogEx(NORMAL, "%s" NOLF, qrcode_getModule(q, x, y) ? "##" : " "); + } + PrintAndLogEx(NORMAL, ""); + } +} + void qrcode_print_matrix_utf8(QRCode *q) { // UTF-8 block characters for better resolution diff --git a/client/src/qrcode/qrcode.h b/client/src/qrcode/qrcode.h index dcb1798bd..73c4ddf90 100644 --- a/client/src/qrcode/qrcode.h +++ b/client/src/qrcode/qrcode.h @@ -73,11 +73,13 @@ extern "C" { uint16_t qrcode_getBufferSize(uint8_t version); +int8_t qrcode_getMinVersionForBytes(uint16_t length, uint8_t ecc); int8_t qrcode_initText(QRCode *qrcode, uint8_t *modules, uint8_t version, uint8_t ecc, const char *data); int8_t qrcode_initBytes(QRCode *qrcode, uint8_t *modules, uint8_t version, uint8_t ecc, uint8_t *data, uint16_t length); bool qrcode_getModule(QRCode *qrcode, uint8_t x, uint8_t y); +void qrcode_print_matrix_ascii(QRCode *q); void qrcode_print_matrix_utf8(QRCode *q); void qrcode_print_matrix_utf8_2x2(QRCode *q); diff --git a/client/src/util.c b/client/src/util.c index 0cb222326..e043a4120 100644 --- a/client/src/util.c +++ b/client/src/util.c @@ -1499,6 +1499,90 @@ size_t str_nlen(const char *src, size_t maxlen) { return len; } +static bool str_regex_atom_matches(char atom, bool escaped, char c) { + if (!escaped && atom == '.') { + return true; + } + return (atom == c); +} + +static bool str_regex_match_here(const char *regexp, const char *text); + +static bool str_regex_match_star(char atom, bool escaped, const char *regexp, const char *text) { + do { + if (str_regex_match_here(regexp, text)) { + return true; + } + } while (*text != '\0' && str_regex_atom_matches(atom, escaped, *text++)); + + return false; +} + +static bool str_regex_match_here(const char *regexp, const char *text) { + if (regexp[0] == '\0') { + return true; + } + + if (regexp[0] == '$' && regexp[1] == '\0') { + return (text[0] == '\0'); + } + + bool escaped = false; + char atom = regexp[0]; + size_t atom_len = 1; + if (regexp[0] == '\\' && regexp[1] != '\0') { + escaped = true; + atom = regexp[1]; + atom_len = 2; + } + + if (regexp[atom_len] == '*') { + return str_regex_match_star(atom, escaped, regexp + atom_len + 1, text); + } + + if (text[0] != '\0' && str_regex_atom_matches(atom, escaped, text[0])) { + return str_regex_match_here(regexp + atom_len, text + 1); + } + + return false; +} + +bool str_regex_match(const char *regexp, const char *text) { + if (regexp[0] == '^') { + return str_regex_match_here(regexp + 1, text); + } + + do { + if (str_regex_match_here(regexp, text)) { + return true; + } + } while (*text++ != '\0'); + + return false; +} + +bool str_regex_match_case_insensitive(const char *regexp, const char *text) { + if (regexp == NULL || text == NULL) { + return false; + } + + char *pattern_lc = str_dup(regexp); + char *text_lc = str_dup(text); + if (pattern_lc == NULL || text_lc == NULL) { + free(pattern_lc); + free(text_lc); + return false; + } + + str_lower(pattern_lc); + str_lower(text_lc); + bool matched = str_regex_match(pattern_lc, text_lc); + + free(pattern_lc); + free(text_lc); + return matched; +} + void str_reverse(char *buf, size_t len) { for (size_t i = 0; i < (len >> 1); i++) { char tmp = buf[i]; diff --git a/client/src/util.h b/client/src/util.h index cb3a1555c..036a67d5c 100644 --- a/client/src/util.h +++ b/client/src/util.h @@ -181,6 +181,12 @@ void str_trim(char *s); char *str_dup(const char *src); char *str_ndup(const char *src, size_t len); size_t str_nlen(const char *src, size_t maxlen); +// Lightweight regex subset: +// - supported metacharacters: '^' (start), '$' (end), '.' (any char), '*' (zero or more) +// - escaping: '\\' to match the following char literally +// - all other regex constructs are currently treated as literal characters +bool str_regex_match(const char *regexp, const char *text); +bool str_regex_match_case_insensitive(const char *regexp, const char *text); int hexstring_to_u96(uint32_t *hi2, uint32_t *hi, uint32_t *lo, const char *str); int binstring_to_u96(uint32_t *hi2, uint32_t *hi, uint32_t *lo, const char *str); diff --git a/doc/aidlist.md b/doc/aidlist.md index 00dec60ca..800644a4b 100644 --- a/doc/aidlist.md +++ b/doc/aidlist.md @@ -15,6 +15,9 @@ Each entry in `client/resources/aidlist.json` must contain all of the fields bel - `Description`: Extra context, disambiguation, references, legacy naming, known usage notes, or deployment-specific remarks. - `Type`: High-level category tag (for example `transport`, `emv`, `gp`, `pacs`, `ndef`). +## Optional fields +- `ResponseContains`: Case-insensitive hex substring matched against the APDU SELECT response (encoded as hex without separators). Use this field when multiple protocols share the same AID and can be distinguished by response content. + Example: ```json { @@ -26,3 +29,16 @@ Example: "Type": "transport" } ``` + +Response-disambiguation example: +```json +{ + "AID": "4F53452E5641532E3031", + "Vendor": "Google", + "Country": "", + "Name": "Google Smart Tap (OSE.VAS.01)", + "Description": "Google Smart Tap", + "Type": "loyalty", + "ResponseContains": "500a416e64726f6964506179" +} +``` diff --git a/include/mifare.h b/include/mifare.h index 6658a2e38..9978ab212 100644 --- a/include/mifare.h +++ b/include/mifare.h @@ -108,6 +108,7 @@ typedef enum ISO14A_COMMAND { ISO14A_CRYPTO1MODE = (1 << 14), ISO14A_SET_WAIT_US = (1 << 15), ISO14A_APPEND_CMAC = (1 << 16), + ISO14A_CLEARTRACE = (1 << 17), } iso14a_command_t; typedef struct { diff --git a/tools/pm3_tests.sh b/tools/pm3_tests.sh index 2fcaaf82d..b6e534b17 100755 --- a/tools/pm3_tests.sh +++ b/tools/pm3_tests.sh @@ -462,9 +462,15 @@ while true; do if ! CheckExecute "reveng readline test" "$CLIENTBIN -c 'reveng -h;reveng -D'" "CRC-64/GO-ISO"; then break; fi if ! CheckExecute "reveng -g test" "$CLIENTBIN -c 'reveng -g abda202c'" "CRC-16/ISO-IEC-14443-3-A"; then break; fi if ! CheckExecute "reveng -w test" "$CLIENTBIN -c 'reveng -w 8 -s 01020304e3 010204039d'" "CRC-8/SMBUS"; then break; fi + if ! CheckExecute "data qrcode ascii test" "$CLIENTBIN -c 'data qrcode -d aa --ascii'" "##"; then break; fi + if ! CheckExecute "data qrcode repeated -d" "$CLIENTBIN -c 'data qrcode -d aa -d bb' 2>&1" "excess option -d\\|--data"; then break; fi + if ! CheckExecute "data qrcode invalid hex" "$CLIENTBIN -c 'data qrcode -d zz' 2>&1" "QR data must contain only hex characters"; then break; fi + if ! CheckExecute "data qrcode odd hex" "$CLIENTBIN -c 'data qrcode -d a' 2>&1" "QR data must contain an even number of hex digits"; then break; fi + if ! CheckExecute "data qrcode spaced hex" "$CLIENTBIN -c 'data qrcode -d \"aa bb\"' 2>&1" "Spaces are not supported; encode a space byte as 20"; then break; fi if ! CheckExecute "mfu pwdgen test" "$CLIENTBIN -c 'hf mfu pwdgen --test'" "Selftest ok"; then break; fi if ! CheckExecute "mfu keygen test" "$CLIENTBIN -c 'hf mfu keygen --uid 11223344556677'" "80 B1 C2 71 D8 A0"; then break; fi if ! CheckExecute "jooki encode test" "$CLIENTBIN -c 'hf jooki encode --test'" "04 28 F4 DA F0 4A 81 \( ok \)"; then break; fi + if ! CheckExecute "analyse regex selftest" "$CLIENTBIN -c 'analyse regex --test'" "Tests \( ok \)"; then break; fi if ! CheckExecute "trace load/list 14a" "$CLIENTBIN -c 'trace load -f traces/hf_14a_mfu.trace; trace list -1 -t 14a;'" "READBLOCK\(8\)"; then break; fi if ! CheckExecute "trace load/list x" "$CLIENTBIN -c 'trace load -f traces/hf_14a_mfu.trace; trace list -x1 -t 14a;'" "0.0101840425"; then break; fi if ! CheckExecute "nfc decode test - oob" "$CLIENTBIN -c 'nfc decode -d DA2010016170706C69636174696F6E2F766E642E626C7565746F6F74682E65702E6F6F62301000649201B96DFB0709466C65782032'" "Flex 2"; then break; fi