diff --git a/CHANGELOG.md b/CHANGELOG.md index 025595e2c..17f7a1583 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ 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] + - Fixed `nfc decode` - now properly handles MFU dump files (@iceman1001) + - Added support for loading Flipper MCT/MFU dump files (@iceman1001) - Changed `data bmap` - now default `-m` is 8 (@iceman1001) - Added support for NTAG424 cards. (@dankar) - Additional fixes to configcard code for keyroll mode based on nfc-iclass output (@Antiklesys) diff --git a/client/src/cmdnfc.c b/client/src/cmdnfc.c index 431464076..70ca53e97 100644 --- a/client/src/cmdnfc.c +++ b/client/src/cmdnfc.c @@ -113,26 +113,45 @@ static int CmdNfcDecode(const char *Cmd) { return res; } - // convert from MFC dump file to a pure NDEF byte array - if (HasMADKey(dump)) { - PrintAndLogEx(SUCCESS, "MFC dump file detected. Converting..."); - uint8_t ndef[4096] = {0}; - uint16_t ndeflen = 0; + uint8_t *tmp = dump; - if (convert_mad_to_arr(dump, bytes_read, ndef, &ndeflen) != PM3_SUCCESS) { - PrintAndLogEx(FAILED, "Failed converting, aborting..."); - free(dump); - return PM3_ESOFT; + // if not MIFARE Classic default sizes, assume its Ultralight/NTAG + if (bytes_read != 4096 || bytes_read != 2048 || bytes_read != 1024 || bytes_read != 320) { + + uint8_t **pd = &tmp; + mfu_df_e df = detect_mfu_dump_format(pd, verbose); + if (df == MFU_DF_OLDBIN) { + tmp += OLD_MFU_DUMP_PREFIX_LENGTH + (4 * 4); + bytes_read -= OLD_MFU_DUMP_PREFIX_LENGTH + ( 4 * 4); + } else if (df == MFU_DF_NEWBIN) { + tmp += MFU_DUMP_PREFIX_LENGTH + (4 * 4); + bytes_read -= MFU_DUMP_PREFIX_LENGTH + ( 4 * 4); } + pd = NULL; - memcpy(dump, ndef, ndeflen); - bytes_read = ndeflen; + } else { + + // convert from MFC dump file to a pure NDEF byte array + if (HasMADKey(tmp)) { + PrintAndLogEx(SUCCESS, "MFC dump file detected. Converting..."); + uint8_t ndef[4096] = {0}; + uint16_t ndeflen = 0; + + if (convert_mad_to_arr(tmp, bytes_read, ndef, &ndeflen) != PM3_SUCCESS) { + PrintAndLogEx(FAILED, "Failed converting, aborting..."); + free(dump); + return PM3_ESOFT; + } + + memcpy(tmp, ndef, ndeflen); + bytes_read = ndeflen; + } } - res = NDEFDecodeAndPrint(dump, bytes_read, verbose); + res = NDEFDecodeAndPrint(tmp, bytes_read, verbose); if (res != PM3_SUCCESS) { PrintAndLogEx(INFO, "Trying to parse NDEF records w/o NDEF header"); - res = NDEFRecordsDecodeAndPrint(dump, bytes_read, verbose); + res = NDEFRecordsDecodeAndPrint(tmp, bytes_read, verbose); } free(dump); diff --git a/client/src/fileutils.c b/client/src/fileutils.c index 10f1af49c..ad2cbd36d 100644 --- a/client/src/fileutils.c +++ b/client/src/fileutils.c @@ -86,6 +86,8 @@ DumpFileType_t getfiletype(const char *filename) { o = DICTIONARY; } else if (str_endswith(s, "mct")) { o = MCT; + } else if (str_endswith(s, "nfc")) { + o = NFC; } else { // mfd, trc, trace is binary o = BIN; @@ -1001,6 +1003,195 @@ int loadFileEML_safe(const char *preferredName, void **pdata, size_t *datalen) { return retval; } +int loadFileNFC_safe(const char *preferredName, void *data, size_t maxdatalen, size_t *datalen, nfc_df_e ft) { + + if (data == NULL) return PM3_EINVARG; + + *datalen = 0; + int retval = PM3_SUCCESS; + + char *path; + int res = searchFile(&path, RESOURCES_SUBDIR, preferredName, "", false); + if (res != PM3_SUCCESS) { + return PM3_EFILE; + } + + FILE *f = fopen(path, "r"); + if (!f) { + PrintAndLogEx(WARNING, "file not found or locked `" _YELLOW_("%s") "`", path); + free(path); + return PM3_EFILE; + } + free(path); + + // 256 + 2 newline chars + 1 null terminator + char line[256 + 2 + 1]; + memset(line, 0, sizeof(line)); + + udata_t udata = (udata_t)data; + int n = 0; + + while (!feof(f)) { + + memset(line, 0, sizeof(line)); + + if (fgets(line, sizeof(line), f) == NULL) { + if (feof(f)) + break; + + fclose(f); + PrintAndLogEx(FAILED, "File reading error."); + return PM3_EFILE; + } + + if (line[0] == '#') + continue; + + strcleanrn(line, sizeof(line)); + str_lower(line); + + + if (str_startswith(line, "uid:")) { + if (ft == NFC_DF_MFC) { + param_gethex_to_eol(line + 4, 0, udata.mfc->card_info.uid, sizeof(udata.mfc->card_info.uid), &n); + } + continue; + } + + if (str_startswith(line, "atqa:")) { + if (ft == NFC_DF_MFC) { + param_gethex_to_eol(line + 5, 0, udata.mfc->card_info.atqa, sizeof(udata.mfc->card_info.atqa), &n); + } + continue; + } + + if (str_startswith(line, "sak:")) { + if (ft == NFC_DF_MFC) { + int sak = 0; + sscanf(line, "sak: %d", &sak); + udata.mfc->card_info.sak = sak & 0xFF; + } + continue; + } + + if (str_startswith(line, "signature:")) { + if (ft == NFC_DF_MFC) { + } else if (ft == NFC_DF_MFU) { + param_gethex_to_eol(line + 11, 0, udata.mfu->signature, sizeof(udata.mfu->signature), &n); + } + continue; + } + + if (str_startswith(line, "mifare version:")) { + if (ft == NFC_DF_MFC) { + } else if (ft == NFC_DF_MFU) { + param_gethex_to_eol(line + 16, 0, udata.mfu->version, sizeof(udata.mfu->version), &n); + } + continue; + } + + if (str_startswith(line, "counter 0:")) { + int no = 0; + sscanf(line, "counter 0: %d", &no); + if (ft == NFC_DF_MFC) { + } else if (ft == NFC_DF_MFU) { + udata.mfu->counter_tearing[0][0] = no & 0xFF; + udata.mfu->counter_tearing[0][1] = no & 0xFF; + udata.mfu->counter_tearing[0][2] = no & 0xFF; + } + continue; + } + + if (str_startswith(line, "tearing 0:")) { + if (ft == NFC_DF_MFC) { + } else if (ft == NFC_DF_MFU) { + sscanf(line, "tearing 0: %02x", &n); + udata.mfu->counter_tearing[0][3] = n & 0xFF; + } + continue; + } + + if (str_startswith(line, "counter 1:")) { + int no = 0; + sscanf(line, "counter 1: %d", &no); + if (ft == NFC_DF_MFC) { + } else if (ft == NFC_DF_MFU) { + udata.mfu->counter_tearing[1][0] = no & 0xFF; + udata.mfu->counter_tearing[1][1] = no & 0xFF; + udata.mfu->counter_tearing[1][2] = no & 0xFF; + } + continue; + } + + if (str_startswith(line, "tearing 1:")) { + if (ft == NFC_DF_MFC) { + } else if (ft == NFC_DF_MFU) { + sscanf(line, "tearing 1: %02x", &n); + udata.mfu->counter_tearing[1][3] = n & 0xFF; + } + continue; + } + + if (str_startswith(line, "counter 2:")) { + int no = 0; + sscanf(line, "counter 2: %d", &no); + if (ft == NFC_DF_MFC) { + } else if (ft == NFC_DF_MFU) { + udata.mfu->counter_tearing[2][0] = no & 0xFF; + udata.mfu->counter_tearing[2][1] = no & 0xFF; + udata.mfu->counter_tearing[2][2] = no & 0xFF; + } + continue; + } + + if (str_startswith(line, "tearing 2:")) { + if (ft == NFC_DF_MFC) { + } else if (ft == NFC_DF_MFU) { + sscanf(line, "tearing 2: %02x", &n); + udata.mfu->counter_tearing[2][3] = n & 0xFF; + } + continue; + } + + if (str_startswith(line, "pages total:")) { + sscanf(line, "pages total: %d", &n); + if (ft == NFC_DF_MFC) { + } else if (ft == NFC_DF_MFU) { + udata.mfu->pages = n; + } + continue; + } + + // Page 0: 04 10 56 CA + if (str_startswith(line, "page ")) { + int pageno = 0; + sscanf(line, "page %d:", &pageno); + + char *p = line; + while (*p++ != ':') {}; + + if (ft == NFC_DF_MFC) { + param_gethex_to_eol(p, 0, udata.mfc->dump + (pageno * MFBLOCK_SIZE), MFBLOCK_SIZE, &n); + udata.mfc->dumplen += MFBLOCK_SIZE; + } else if (ft == NFC_DF_MFU) { + param_gethex_to_eol(p, 0, udata.mfu->data + (pageno * MFU_BLOCK_SIZE), MFU_BLOCK_SIZE, &n); + *datalen += MFU_BLOCK_SIZE; + } + continue; + } + } + + // add header length + if (ft == NFC_DF_MFC) { + } else if (ft == NFC_DF_MFU) { + *datalen += MFU_DUMP_PREFIX_LENGTH; + } + + fclose(f); + PrintAndLogEx(SUCCESS, "loaded " _YELLOW_("%zu") " bytes from NFC file `" _YELLOW_("%s") "`", *datalen, preferredName); + return retval; +} + int loadFileMCT_safe(const char *preferredName, void **pdata, size_t *datalen) { char *path; int res = searchFile(&path, RESOURCES_SUBDIR, preferredName, "", false); @@ -1926,7 +2117,7 @@ int loadFileBinaryKey(const char *preferredName, const char *suffix, void **keya return PM3_SUCCESS; } -mfu_df_e detect_mfu_dump_format(uint8_t **dump, size_t *dumplen, bool verbose) { +mfu_df_e detect_mfu_dump_format(uint8_t **dump, bool verbose) { mfu_df_e retval = MFU_DF_UNKNOWN; uint8_t bcc0, bcc1; @@ -1979,6 +2170,99 @@ mfu_df_e detect_mfu_dump_format(uint8_t **dump, size_t *dumplen, bool verbose) { return retval; } +nfc_df_e detect_nfc_dump_format(const char *preferredName, bool verbose) { + + char *path; + int res = searchFile(&path, RESOURCES_SUBDIR, preferredName, "", false); + if (res != PM3_SUCCESS) { + return PM3_EFILE; + } + + FILE *f = fopen(path, "r"); + if (!f) { + PrintAndLogEx(WARNING, "file not found or locked `" _YELLOW_("%s") "`", path); + free(path); + return PM3_EFILE; + } + free(path); + + nfc_df_e retval = NFC_DF_UNKNOWN; + + char line[256]; + memset(line, 0, sizeof(line)); + + while (!feof(f)) { + + memset(line, 0, sizeof(line)); + + if (fgets(line, sizeof(line), f) == NULL) { + if (feof(f)) { + break; + } + + fclose(f); + PrintAndLogEx(FAILED, "File reading error."); + return PM3_EFILE; + } + + strcleanrn(line, sizeof(line)); + str_lower(line); + + if (str_startswith(line, "device type: ntag")) { + retval = NFC_DF_MFU; + break; + } + if (str_startswith(line, "device type: mifare classic")) { + retval = NFC_DF_MFC; + break; + } + if (str_startswith(line, "device type: mifare desfire")) { + retval = NFC_DF_MFDES; + break; + } + if (str_startswith(line, "device type: iso14443-3a")) { + retval = NFC_DF_14_3A; + break; + } + if (str_startswith(line, "device type: iso14443-3b")) { + retval = NFC_DF_14_3B; + break; + } + if (str_startswith(line, "device type: iso14443-4a")) { + retval = NFC_DF_14_4A; + break; + } + } + fclose(f); + + if (verbose) { + switch (retval) { + case NFC_DF_MFU: + PrintAndLogEx(INFO, "detected MIFARE Ultralight / NTAG based dump format"); + break; + case NFC_DF_MFC: + PrintAndLogEx(INFO, "detected MIFARE Classic based dump format"); + break; + case NFC_DF_MFDES: + PrintAndLogEx(INFO, "detected MIFARE DESFire based dump format"); + break; + case NFC_DF_14_3A: + PrintAndLogEx(INFO, "detected ISO14443-3A based dump format. No data available"); + break; + case NFC_DF_14_3B: + PrintAndLogEx(INFO, "detected ISO14443-3B based dump format. No data available"); + break; + case NFC_DF_14_4A: + PrintAndLogEx(INFO, "detected ISO14443-4A based dump format. No data available"); + break; + case NFC_DF_UNKNOWN: + PrintAndLogEx(WARNING, "failed to detected dump format"); + break; + } + } + return retval; +} + static int convert_plain_mfu_dump(uint8_t **dump, size_t *dumplen, bool verbose) { mfu_dump_t *mfu = (mfu_dump_t *) calloc(sizeof(mfu_dump_t), sizeof(uint8_t)); @@ -2055,7 +2339,7 @@ int convert_mfu_dump_format(uint8_t **dump, size_t *dumplen, bool verbose) { return PM3_EINVARG; } - mfu_df_e res = detect_mfu_dump_format(dump, dumplen, verbose); + mfu_df_e res = detect_mfu_dump_format(dump, verbose); switch (res) { case MFU_DF_NEWBIN: @@ -2419,15 +2703,38 @@ int pm3_load_dump(const char *fn, void **pdump, size_t *dumplen, size_t maxdumpl break; } case DICTIONARY: { - PrintAndLogEx(ERR, "Only BIN/EML/JSON formats allowed"); + PrintAndLogEx(ERR, "Only Payload[pos] != 0x10) { n -= 1; - pos -= 1; + pos += 1; continue; } @@ -775,6 +775,16 @@ static int ndefDecodeMime_wifi_wsc(NDEFHeader_t *ndef) { pos += 2; pos += len; } + + // unknown the length. + if (memcmp(&ndef->Payload[pos], "\x10\x3C", 2) == 0) { + uint8_t len = 3; + PrintAndLogEx(INFO, "Unknown......... %s", sprint_hex(&ndef->Payload[pos + 2], len)); + n -= 2; + n -= len; + pos += 2; + pos += len; + } } /* @@ -1182,6 +1192,7 @@ int NDEFDecodeAndPrint(uint8_t *ndef, size_t ndefLen, bool verbose) { PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "--- " _CYAN_("NDEF parsing") " ----------------"); while (indx < ndefLen) { + switch (ndef[indx]) { case 0x00: { indx++; @@ -1246,8 +1257,9 @@ int NDEFDecodeAndPrint(uint8_t *ndef, size_t ndefLen, bool verbose) { PrintAndLogEx(SUCCESS, "Found NDEF message ( " _YELLOW_("%u") " bytes )", len); int res = NDEFRecordsDecodeAndPrint(&ndef[indx], len, verbose); - if (res != PM3_SUCCESS) + if (res != PM3_SUCCESS) { return res; + } } indx += len; @@ -1269,9 +1281,9 @@ int NDEFDecodeAndPrint(uint8_t *ndef, size_t ndefLen, bool verbose) { return PM3_SUCCESS; } default: { - if (verbose) + if (verbose) { PrintAndLogEx(ERR, "unknown tag 0x%02x", ndef[indx]); - + } return PM3_ESOFT; } }