diff --git a/CHANGELOG.md b/CHANGELOG.md index 59959b4893..7fb80a57a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,8 +33,11 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac - Add Mifare Classic EV1 signature write support to gen4 magic tag lua script (@augustozanellato) - Added XOR key extraction and flag to Guardall G-Prox II (@GuruSteve) - Changed verbiage on `hf iclass info` KeyAccess area to be congruent with AA1 and AA2 areas (@GuruSteve) - - Added `hf legic info` command for other sources: `hf legic einfo`, `hf legic view` (@0xdeb) - - + - Changed `hf mf view` now you can view Mifare Classic memory without dumping the card (@Alejandro12120) + - Added `hf legic info` command for other sources (@0xdeb) + - Added `hf legic einfo` - views emulator menory (@0xdeb) + - Changed `hf legic view` - now also print the decoded info of the dump file (@0xdeb) + ## [Nitride.4.16191][2023-01-29] - Changed `build_all_firmwares.sh` to fit GENERIC 256kb firmware images (@doegox) diff --git a/client/src/cmdhfmf.c b/client/src/cmdhfmf.c index d5a629e901..4e8c576d86 100644 --- a/client/src/cmdhfmf.c +++ b/client/src/cmdhfmf.c @@ -787,79 +787,22 @@ static int FastDumpWithEcFill(uint8_t numsectors) { return PM3_SUCCESS; } -static int CmdHF14AMfDump(const char *Cmd) { - CLIParserContext *ctx; - CLIParserInit(&ctx, "hf mf dump", - "Dump MIFARE Classic tag to binary file\n" - "If no given, UID will be used as filename", - "hf mf dump --mini --> MIFARE Mini\n" - "hf mf dump --1k --> MIFARE Classic 1k\n" - "hf mf dump --2k --> MIFARE 2k\n" - "hf mf dump --4k --> MIFARE 4k\n" - "hf mf dump --keys hf-mf-066C8B78-key.bin --> MIFARE 1k with keys from specified file\n"); - - void *argtable[] = { - arg_param_begin, - arg_str0("f", "file", "", "filename of dump"), - arg_str0("k", "keys", "", "filename of keys"), - arg_lit0(NULL, "mini", "MIFARE Classic Mini / S20"), - arg_lit0(NULL, "1k", "MIFARE Classic 1k / S50 (def)"), - arg_lit0(NULL, "2k", "MIFARE Classic/Plus 2k"), - arg_lit0(NULL, "4k", "MIFARE Classic 4k / S70"), - arg_param_end - }; - CLIExecWithReturn(ctx, Cmd, argtable, true); - - int datafnlen = 0; - char dataFilename[FILE_PATH_SIZE] = {0}; - CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)dataFilename, FILE_PATH_SIZE, &datafnlen); - - int keyfnlen = 0; - char keyFilename[FILE_PATH_SIZE] = {0}; - CLIParamStrToBuf(arg_get_str(ctx, 2), (uint8_t *)keyFilename, FILE_PATH_SIZE, &keyfnlen); - - bool m0 = arg_get_lit(ctx, 3); - bool m1 = arg_get_lit(ctx, 4); - bool m2 = arg_get_lit(ctx, 5); - bool m4 = arg_get_lit(ctx, 6); - - CLIParserFree(ctx); - - uint64_t t1 = msclock(); - - // validations - if ((m0 + m1 + m2 + m4) > 1) { - PrintAndLogEx(WARNING, "Only specify one MIFARE Type"); - return PM3_EINVARG; - } else if ((m0 + m1 + m2 + m4) == 0) { - m1 = true; - } - - uint8_t numSectors = MIFARE_1K_MAXSECTOR; - - if (m0) { - numSectors = MIFARE_MINI_MAXSECTOR; - } else if (m1) { - numSectors = MIFARE_1K_MAXSECTOR; - } else if (m2) { - numSectors = MIFARE_2K_MAXSECTOR; - } else if (m4) { - numSectors = MIFARE_4K_MAXSECTOR; - } else { - PrintAndLogEx(WARNING, "Please specify a MIFARE Type"); - return PM3_EINVARG; - } - +/* Reads data from tag + * @param card: (output) card info + * @param carddata: (output) card data + * @param numSectors: size of the card + * @param keyFileName: filename containing keys or NULL. +*/ +static int mfc_read_tag(iso14a_card_select_t *card, uint8_t carddata[256][16], uint8_t numSectors, char *keyFilename){ uint8_t sectorNo, blockNo; uint8_t keyA[40][6]; uint8_t keyB[40][6]; uint8_t rights[40][4]; - uint8_t carddata[256][16]; FILE *f; PacketResponseNG resp; - char *fptr; + char *fptr = NULL ; // Select card to get UID/UIDLEN/ATQA/SAK information clearCommandBuffer(); @@ -876,20 +819,18 @@ static int CmdHF14AMfDump(const char *Cmd) { } // store card info - iso14a_card_select_t card; - memcpy(&card, (iso14a_card_select_t *)resp.data.asBytes, sizeof(iso14a_card_select_t)); + memcpy(card, (iso14a_card_select_t *)resp.data.asBytes, sizeof(iso14a_card_select_t)); - if (keyFilename[0] == 0x00) { + if (keyFilename == NULL || keyFilename[0] == '\0') { fptr = GenerateFilename("hf-mf-", "-key.bin"); if (fptr == NULL) return PM3_ESOFT; - strncpy(keyFilename, fptr, sizeof(keyFilename) - 1); - free(fptr); + keyFilename = fptr ; } if ((f = fopen(keyFilename, "rb")) == NULL) { - PrintAndLogEx(WARNING, "Could not find file " _YELLOW_("%s"), keyFilename); + PrintAndLogEx(WARNING, "Could not find key file " _YELLOW_("%s"), keyFilename); return PM3_EFILE; } @@ -900,7 +841,7 @@ static int CmdHF14AMfDump(const char *Cmd) { for (sectorNo = 0; sectorNo < numSectors; sectorNo++) { bytes_read = fread(keyA[sectorNo], 1, MFKEY_SIZE, f); if (bytes_read != MFKEY_SIZE) { - PrintAndLogEx(ERR, "File reading error."); + PrintAndLogEx(ERR, "Key file reading error."); fclose(f); return PM3_EFILE; } @@ -910,12 +851,19 @@ static int CmdHF14AMfDump(const char *Cmd) { for (sectorNo = 0; sectorNo < numSectors; sectorNo++) { bytes_read = fread(keyB[sectorNo], 1, MFKEY_SIZE, f); if (bytes_read != MFKEY_SIZE) { - PrintAndLogEx(ERR, "File reading error."); + PrintAndLogEx(ERR, "Key file reading error."); fclose(f); return PM3_EFILE; } } + if (fptr != NULL) { + if (keyFilename == fptr) { + keyFilename = NULL ; + } + free(fptr); + fptr = NULL; + } fclose(f); PrintAndLogEx(INFO, "Reading sector access bits..."); @@ -1025,19 +973,10 @@ static int CmdHF14AMfDump(const char *Cmd) { if (received) { uint8_t *data = resp.data.asBytes; if (blockNo == mfNumBlocksPerSector(sectorNo) - 1) { // sector trailer. Fill in the keys. - data[0] = (keyA[sectorNo][0]); - data[1] = (keyA[sectorNo][1]); - data[2] = (keyA[sectorNo][2]); - data[3] = (keyA[sectorNo][3]); - data[4] = (keyA[sectorNo][4]); - data[5] = (keyA[sectorNo][5]); - - data[10] = (keyB[sectorNo][0]); - data[11] = (keyB[sectorNo][1]); - data[12] = (keyB[sectorNo][2]); - data[13] = (keyB[sectorNo][3]); - data[14] = (keyB[sectorNo][4]); - data[15] = (keyB[sectorNo][5]); + for (int i=0; i<6; i++) { + data[i] = (keyA[sectorNo][i]); + data[10+i] = (keyB[sectorNo][i]); + } } if (resp.status == PM3_SUCCESS) { memcpy(carddata[mfFirstBlockOfSector(sectorNo) + blockNo], data, 16); @@ -1051,12 +990,84 @@ static int CmdHF14AMfDump(const char *Cmd) { } } - PrintAndLogEx(SUCCESS, "time: %" PRIu64 " seconds\n", (msclock() - t1) / 1000); - PrintAndLogEx(SUCCESS, "\nSucceeded in dumping all blocks"); + return PM3_SUCCESS ; +} + +static int CmdHF14AMfDump(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mf dump", + "Dump MIFARE Classic tag to binary file\n" + "If no given, UID will be used as filename", + "hf mf dump --mini --> MIFARE Mini\n" + "hf mf dump --1k --> MIFARE Classic 1k\n" + "hf mf dump --2k --> MIFARE 2k\n" + "hf mf dump --4k --> MIFARE 4k\n" + "hf mf dump --keys hf-mf-066C8B78-key.bin --> MIFARE 1k with keys from specified file\n"); + + void *argtable[] = { + arg_param_begin, + arg_str0("f", "file", "", "filename of dump"), + arg_str0("k", "keys", "", "filename of keys"), + arg_lit0(NULL, "mini", "MIFARE Classic Mini / S20"), + arg_lit0(NULL, "1k", "MIFARE Classic 1k / S50 (def)"), + arg_lit0(NULL, "2k", "MIFARE Classic/Plus 2k"), + arg_lit0(NULL, "4k", "MIFARE Classic 4k / S70"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + int datafnlen = 0; + char dataFilename[FILE_PATH_SIZE] = {0}; + CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)dataFilename, FILE_PATH_SIZE, &datafnlen); + + int keyfnlen = 0; + char keyFilename[FILE_PATH_SIZE] = {0}; + CLIParamStrToBuf(arg_get_str(ctx, 2), (uint8_t *)keyFilename, FILE_PATH_SIZE, &keyfnlen); + + bool m0 = arg_get_lit(ctx, 3); + bool m1 = arg_get_lit(ctx, 4); + bool m2 = arg_get_lit(ctx, 5); + bool m4 = arg_get_lit(ctx, 6); + + CLIParserFree(ctx); + + uint64_t t1 = msclock(); + + // validations + if ((m0 + m1 + m2 + m4) > 1) { + PrintAndLogEx(WARNING, "Only specify one MIFARE Type"); + return PM3_EINVARG; + } else if ((m0 + m1 + m2 + m4) == 0) { + m1 = true; + } + + uint8_t numSectors = MIFARE_1K_MAXSECTOR; + + if (m0) { + numSectors = MIFARE_MINI_MAXSECTOR; + } else if (m1) { + numSectors = MIFARE_1K_MAXSECTOR; + } else if (m2) { + numSectors = MIFARE_2K_MAXSECTOR; + } else if (m4) { + numSectors = MIFARE_4K_MAXSECTOR; + } else { + PrintAndLogEx(WARNING, "Please specify a MIFARE Type"); + return PM3_EINVARG; + } + + // read card + iso14a_card_select_t card ; + uint8_t carddata[256][16]; + int res=mfc_read_tag(&card, carddata, numSectors, keyFilename); + if (res != PM3_SUCCESS) + return res; + + PrintAndLogEx(SUCCESS, "time: %" PRIu64 " seconds\n", (msclock() - t1) / 1000); if (strlen(dataFilename) < 1) { - fptr = GenerateFilename("hf-mf-", "-dump"); + char *fptr = GenerateFilename("hf-mf-", "-dump"); if (fptr == NULL) return PM3_ESOFT; @@ -6853,41 +6864,113 @@ static int CmdHF14AMfView(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mf view", - "Print a MIFARE Classic dump file (bin/eml/json)", - "hf mf view -f hf-mf-01020304-dump.bin" + "View Mifare classic card memory or dump.\n", + "\n" + "`--file` param is used for dump filename.\n" + "`--kfn` param is used for key filename.\n" + "\n" + "hf mf view --file hf-mf-11223344-dump.bin\n" + "hf mf view --kfn hf-mf-11223344-key.bin\n" + "hf mf view --2k --kfn hf-mf-11223344-key.bin" ); + void *argtable[] = { arg_param_begin, - arg_str1("f", "file", "", "filename of dump"), + arg_str0("f", "file", "", "specify dump filename (bin/eml/json)"), + arg_str0("k", "kfn", "", "key filename"), + arg_lit0(NULL, "mini", "MIFARE Classic Mini / S20"), + arg_lit0(NULL, "1k", "MIFARE Classic 1k / S50 (def)"), + arg_lit0(NULL, "2k", "MIFARE Classic/Plus 2k"), + arg_lit0(NULL, "4k", "MIFARE Classic 4k / S70"), arg_lit0("v", "verbose", "verbose output"), + arg_param_end }; - CLIExecWithReturn(ctx, Cmd, argtable, false); - int fnlen = 0; - char filename[FILE_PATH_SIZE]; - CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); - bool verbose = arg_get_lit(ctx, 2); + CLIExecWithReturn(ctx, Cmd, argtable, true); + + int dumplen = 0; + char dumpname[FILE_PATH_SIZE]; + CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)dumpname, FILE_PATH_SIZE, &dumplen); + + int keylen = 0; + char keyname[FILE_PATH_SIZE]; + CLIParamStrToBuf(arg_get_str(ctx, 2), (uint8_t *)keyname, FILE_PATH_SIZE, &keylen); + + bool verbose = arg_get_lit(ctx, 7); + + bool m0 = arg_get_lit(ctx, 3); + bool m1 = arg_get_lit(ctx, 4); + bool m2 = arg_get_lit(ctx, 5); + bool m4 = arg_get_lit(ctx, 6); + CLIParserFree(ctx); - // read dump file uint8_t *dump = NULL; - size_t bytes_read = 0; - int res = pm3_load_dump(filename, (void **)&dump, &bytes_read, (MFBLOCK_SIZE * MIFARE_4K_MAXBLOCK)); - if (res != PM3_SUCCESS) { - return res; - } + uint8_t carddata[256][16]; + uint16_t block_cnt = 0; + size_t card_size = 0; - uint16_t block_cnt = MIN(MIFARE_1K_MAXBLOCK, (bytes_read / MFBLOCK_SIZE)); - if (bytes_read == 320) - block_cnt = MIFARE_MINI_MAXBLOCK; - else if (bytes_read == 2048) - block_cnt = MIFARE_2K_MAXBLOCK; - else if (bytes_read == 4096) - block_cnt = MIFARE_4K_MAXBLOCK; + if(dumplen != 0) { + // Read dump file + size_t bytes_read = 0; + int res = pm3_load_dump(dumpname, (void **)&dump, &bytes_read, (MFBLOCK_SIZE * MIFARE_4K_MAXBLOCK)); + if (res != PM3_SUCCESS) { + return res; + } + block_cnt = MIN(MIFARE_1K_MAXBLOCK, (bytes_read / MFBLOCK_SIZE)); + if (bytes_read == 320) + block_cnt = MIFARE_MINI_MAXBLOCK; + else if (bytes_read == 2048) + block_cnt = MIFARE_2K_MAXBLOCK; + else if (bytes_read == 4096) + block_cnt = MIFARE_4K_MAXBLOCK; - if (verbose) { - PrintAndLogEx(INFO, "File: " _YELLOW_("%s"), filename); - PrintAndLogEx(INFO, "File size %zu bytes, file blocks %d (0x%x)", bytes_read, block_cnt, block_cnt); + card_size = bytes_read ; + + if (verbose) { + PrintAndLogEx(INFO, "File: " _YELLOW_("%s"), dumpname); + PrintAndLogEx(INFO, "File size %zu bytes, file blocks %d (0x%x)", bytes_read, block_cnt, block_cnt); + } + } else { + // Read from card + if (g_session.pm3_present == false) { + PrintAndLogEx(WARNING, "Device offline\n"); + return PM3_ENODATA; + } + + // validations + if ((m0 + m1 + m2 + m4) > 1) { + PrintAndLogEx(WARNING, "Only specify one MIFARE Type"); + return PM3_EINVARG; + } else if ((m0 + m1 + m2 + m4) == 0) { + m1 = true; + } + uint8_t numSectors = MIFARE_1K_MAXSECTOR; + if (m0) { + numSectors = MIFARE_MINI_MAXSECTOR; + card_size = 320; + } else if (m1) { + numSectors = MIFARE_1K_MAXSECTOR; + card_size = 1024; + } else if (m2) { + numSectors = MIFARE_2K_MAXSECTOR; + card_size = 2048; + } else if (m4) { + numSectors = MIFARE_4K_MAXSECTOR; + card_size = 4096; + } else { + PrintAndLogEx(WARNING, "Please specify a MIFARE Type"); + return PM3_EINVARG; + } + block_cnt = numSectors * 4 ; + + iso14a_card_select_t card ; + int res=mfc_read_tag(&card, carddata, numSectors, NULL); + if (res != PM3_SUCCESS) + return res; + + // flatten carddata into dump + dump=(uint8_t *)carddata; } mf_print_blocks(block_cnt, dump, verbose); @@ -6906,7 +6989,7 @@ static int CmdHF14AMfView(const char *Cmd) { // decode MAD uint16_t mad[7 + 8 + 8 + 8 + 8] = {0}; size_t madlen = 0; - res = MADDecode(dump, NULL, mad, &madlen, false); + int res = MADDecode(dump, NULL, mad, &madlen, false); if (res != PM3_SUCCESS) { PrintAndLogEx(ERR, "can't decode MAD"); return res; @@ -6918,7 +7001,7 @@ static int CmdHF14AMfView(const char *Cmd) { } UDATA; // allocate memory UDATA d; - d.bytes = calloc(bytes_read, sizeof(uint8_t)); + d.bytes = calloc(card_size, sizeof(uint8_t)); if (d.bytes == NULL) { return PM3_EMALLOC; } @@ -6945,8 +7028,10 @@ static int CmdHF14AMfView(const char *Cmd) { vigik_annotate(d.vigik); free(d.bytes); } + if (dump != (uint8_t *)carddata) { + free(dump); + } - free(dump); return PM3_SUCCESS; } @@ -7792,7 +7877,7 @@ static command_t CommandTable[] = { {"restore", CmdHF14AMfRestore, IfPm3Iso14443a, "Restore MIFARE Classic binary file to tag"}, {"setmod", CmdHf14AMfSetMod, IfPm3Iso14443a, "Set MIFARE Classic EV1 load modulation strength"}, {"value", CmdHF14AMfValue, AlwaysAvailable, "Value blocks"}, - {"view", CmdHF14AMfView, AlwaysAvailable, "Display content from tag dump file"}, + {"view", CmdHF14AMfView, AlwaysAvailable, "Display content from tag"}, {"wipe", CmdHF14AMfWipe, IfPm3Iso14443a, "Wipe card to zeros and default keys/acc"}, {"wrbl", CmdHF14AMfWrBl, IfPm3Iso14443a, "Write MIFARE Classic block"}, {"-----------", CmdHelp, IfPm3Iso14443a, "----------------------- " _CYAN_("simulation") " -----------------------"},