diff --git a/CHANGELOG.md b/CHANGELOG.md index 82d32525c..65de3e81c 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] + - Fix `hf mf dump/auto/csave` MFC JSON dumps - save real ATQA/SAK (@doegox) - Added option to load raw NDEF to `nfc decode` command (@doegox) - Added option to save raw NDEF to all `hf * ndefread` commands (@doegox) - Changed `hf 14a info` - different FUDAN clone detection (@iceman1001) diff --git a/client/src/cmdhfmf.c b/client/src/cmdhfmf.c index 0f12be2ee..c699e9e0d 100644 --- a/client/src/cmdhfmf.c +++ b/client/src/cmdhfmf.c @@ -623,6 +623,24 @@ static int CmdHF14AMfDump(const char *Cmd) { char *fptr; + // Select card to get UID/UIDLEN/ATQA/SAK information + clearCommandBuffer(); + SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT, 0, 0, NULL, 0); + if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) { + PrintAndLogEx(WARNING, "iso14443a card select timeout"); + return PM3_ETIMEOUT; + } + + uint64_t select_status = resp.oldarg[0]; + if (select_status == 0) { + PrintAndLogEx(WARNING, "iso14443a card select failed"); + return select_status; + } + + // store card info + iso14a_card_select_t card; + memcpy(&card, (iso14a_card_select_t *)resp.data.asBytes, sizeof(iso14a_card_select_t)); + if (keyFilename[0] == 0x00) { fptr = GenerateFilename("hf-mf-", "-key.bin"); if (fptr == NULL) @@ -799,7 +817,12 @@ static int CmdHF14AMfDump(const char *Cmd) { saveFile(dataFilename, ".bin", (uint8_t *)carddata, bytes); saveFileEML(dataFilename, (uint8_t *)carddata, bytes, MFBLOCK_SIZE); - saveFileJSON(dataFilename, jsfCardMemory, (uint8_t *)carddata, bytes, NULL); + + iso14a_mf_extdump_t xdump; + xdump.card_info = card; + xdump.dump = (uint8_t *)carddata; + xdump.dumplen = bytes; + saveFileJSON(dataFilename, jsfCardMemory, (uint8_t*)&xdump, sizeof(xdump), NULL); return PM3_SUCCESS; } @@ -1972,6 +1995,25 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { // ------------------------------ + // Select card to get UID/UIDLEN/ATQA/SAK information + clearCommandBuffer(); + SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT, 0, 0, NULL, 0); + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) { + PrintAndLogEx(WARNING, "iso14443a card select timeout"); + return PM3_ETIMEOUT; + } + + uint64_t select_status = resp.oldarg[0]; + if (select_status == 0) { + PrintAndLogEx(WARNING, "iso14443a card select failed"); + return select_status; + } + + // store card info + iso14a_card_select_t card; + memcpy(&card, (iso14a_card_select_t *)resp.data.asBytes, sizeof(iso14a_card_select_t)); + // create/initialize key storage structure uint32_t e_sector_size = sector_cnt > sectorno ? sector_cnt : sectorno + 1; res = initSectorTable(&e_sector, e_sector_size); @@ -2314,7 +2356,6 @@ noValidKeyFound: clearCommandBuffer(); SendCommandNG(CMD_HF_MIFARE_READBL, (uint8_t *)&payload, sizeof(mf_readblock_t)); - PacketResponseNG resp; if (!WaitForResponseTimeout(CMD_HF_MIFARE_READBL, &resp, 1500)) goto skipReadBKey; if (resp.status != PM3_SUCCESS) goto skipReadBKey; @@ -2558,7 +2599,11 @@ all_found: saveFile(filename, ".bin", dump, bytes); saveFileEML(filename, dump, bytes, MFBLOCK_SIZE); - saveFileJSON(filename, jsfCardMemory, dump, bytes, NULL); + iso14a_mf_extdump_t xdump; + xdump.card_info = card; + xdump.dump = dump; + xdump.dumplen = bytes; + saveFileJSON(filename, jsfCardMemory, (uint8_t*)&xdump, sizeof(xdump), NULL); // Generate and show statistics t1 = msclock() - t1; @@ -3942,7 +3987,28 @@ static int CmdHF14AMfESave(const char *Cmd) { saveFile(filename, ".bin", dump, bytes); saveFileEML(filename, dump, bytes, MFBLOCK_SIZE); - saveFileJSON(filename, jsfCardMemory, dump, bytes, NULL); + + iso14a_mf_extdump_t xdump = {0}; + xdump.card_info.ats_len = 0; + // Check for 4 bytes uid: bcc corrected and single size uid bits in ATQA + if ((dump[0] ^ dump[1] ^ dump[2] ^ dump[3]) == dump[4] && (dump[6] & 0xc0) == 0) { + xdump.card_info.uidlen = 4; + memcpy(xdump.card_info.uid, dump, xdump.card_info.uidlen); + xdump.card_info.sak = dump[5]; + memcpy(xdump.card_info.atqa, &dump[6], sizeof(xdump.card_info.atqa)); + } + // Check for 7 bytes UID: double size uid bits in ATQA + else if ((dump[8] & 0xc0) == 0x40) { + xdump.card_info.uidlen = 7; + memcpy(xdump.card_info.uid, dump, xdump.card_info.uidlen); + xdump.card_info.sak = dump[7]; + memcpy(xdump.card_info.atqa, &dump[8], sizeof(xdump.card_info.atqa)); + } else { + PrintAndLogEx(WARNING, "Invalid dump. UID/SAK/ATQA not found"); + } + xdump.dump = dump; + xdump.dumplen = bytes; + saveFileJSON(filename, jsfCardMemory, (uint8_t*)&xdump, sizeof(xdump), NULL); free(dump); return PM3_SUCCESS; } @@ -4775,7 +4841,11 @@ static int CmdHF14AMfCSave(const char *Cmd) { saveFile(filename, ".bin", dump, bytes); saveFileEML(filename, dump, bytes, MFBLOCK_SIZE); - saveFileJSON(filename, jsfCardMemory, dump, bytes, NULL); + iso14a_mf_extdump_t xdump; + xdump.card_info = card; + xdump.dump = dump; + xdump.dumplen = bytes; + saveFileJSON(filename, jsfCardMemory, (uint8_t*)&xdump, sizeof(xdump), NULL); free(dump); return PM3_SUCCESS; } diff --git a/client/src/fileutils.c b/client/src/fileutils.c index c83d8f462..08fe3b94d 100644 --- a/client/src/fileutils.c +++ b/client/src/fileutils.c @@ -375,29 +375,25 @@ int saveFileJSONex(const char *preferredName, JSONFileType ftype, uint8_t *data, break; } case jsfCardMemory: { + iso14a_mf_extdump_t* xdump = (iso14a_mf_extdump_t*) data; JsonSaveStr(root, "FileType", "mfcard"); - for (size_t i = 0; i < (datalen / 16); i++) { + JsonSaveBufAsHexCompact(root, "$.Card.UID", xdump->card_info.uid, xdump->card_info.uidlen); + JsonSaveBufAsHexCompact(root, "$.Card.ATQA", xdump->card_info.atqa, 2); + JsonSaveBufAsHexCompact(root, "$.Card.SAK", &(xdump->card_info.sak), 1); + for (size_t i = 0; i < (xdump->dumplen / 16); i++) { char path[PATH_MAX_LENGTH] = {0}; sprintf(path, "$.blocks.%zu", i); - JsonSaveBufAsHexCompact(root, path, &data[i * 16], 16); - - if (i == 0) { - JsonSaveBufAsHexCompact(root, "$.Card.UID", &data[0], 4); - JsonSaveBufAsHexCompact(root, "$.Card.SAK", &data[5], 1); - JsonSaveBufAsHexCompact(root, "$.Card.ATQA", &data[6], 2); - } - + JsonSaveBufAsHexCompact(root, path, &xdump->dump[i * 16], 16); if (mfIsSectorTrailer(i)) { - snprintf(path, sizeof(path), "$.SectorKeys.%d.KeyA", mfSectorNum(i)); - JsonSaveBufAsHexCompact(root, path, &data[i * 16], 6); + JsonSaveBufAsHexCompact(root, path, &xdump->dump[i * 16], 6); snprintf(path, sizeof(path), "$.SectorKeys.%d.KeyB", mfSectorNum(i)); - JsonSaveBufAsHexCompact(root, path, &data[i * 16 + 10], 6); + JsonSaveBufAsHexCompact(root, path, &xdump->dump[i * 16 + 10], 6); - uint8_t *adata = &data[i * 16 + 6]; + uint8_t *adata = &xdump->dump[i * 16 + 6]; snprintf(path, sizeof(path), "$.SectorKeys.%d.AccessConditions", mfSectorNum(i)); - JsonSaveBufAsHexCompact(root, path, &data[i * 16 + 6], 4); + JsonSaveBufAsHexCompact(root, path, &xdump->dump[i * 16 + 6], 4); snprintf(path, sizeof(path), "$.SectorKeys.%d.AccessConditionsText.block%zu", mfSectorNum(i), i - 3); JsonSaveStr(root, path, mfGetAccessConditionsDesc(0, adata)); diff --git a/include/mifare.h b/include/mifare.h index e38c7eb45..1c8fc7f42 100644 --- a/include/mifare.h +++ b/include/mifare.h @@ -48,6 +48,12 @@ typedef struct { uint8_t ats[256]; } PACKED iso14a_card_select_t; +typedef struct { + iso14a_card_select_t card_info; + uint8_t *dump; + uint16_t dumplen; +} iso14a_mf_extdump_t; + typedef enum ISO14A_COMMAND { ISO14A_CONNECT = (1 << 0), ISO14A_NO_DISCONNECT = (1 << 1),