//----------------------------------------------------------------------------- // Copyright (C) 2011,2012 Merlok // // 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 // the license. //----------------------------------------------------------------------------- // High frequency MIFARE commands //----------------------------------------------------------------------------- #include "cmdhfmf.h" #include #include "cmdparser.h" // command_t #include "commonutil.h" // ARRAYLEN #include "comms.h" // clearCommandBuffer #include "fileutils.h" #include "cmdtrace.h" #include "mifare/mifaredefault.h" // mifare default key array #include "cliparser.h" // argtable #include "hardnested_bf_core.h" // SetSIMDInstr #include "mifare/mad.h" #include "mifare/ndef.h" #include "protocols.h" #include "util_posix.h" // msclock #include "cmdhfmfhard.h" #include "des.h" // des ecb #include "crapto1/crapto1.h" // prng_successor #include "cmdhf14a.h" // exchange APDU #include "crypto/libpcrypto.h" #define MFBLOCK_SIZE 16 #define MIFARE_4K_MAXBLOCK 256 #define MIFARE_2K_MAXBLOCK 128 #define MIFARE_1K_MAXBLOCK 64 #define MIFARE_MINI_MAXBLOCK 20 #define MIFARE_MINI_MAXSECTOR 5 #define MIFARE_1K_MAXSECTOR 16 #define MIFARE_2K_MAXSECTOR 32 #define MIFARE_4K_MAXSECTOR 40 static int CmdHelp(const char *Cmd); /* static int usage_hf14_keybrute(void) { PrintAndLogEx(NORMAL, "J_Run's 2nd phase of multiple sector nested authentication key recovery"); PrintAndLogEx(NORMAL, "You have a known 4 last bytes of a key recovered with mf_nonce_brute tool."); PrintAndLogEx(NORMAL, "First 2 bytes of key will be bruteforced"); PrintAndLogEx(NORMAL, ""); PrintAndLogEx(NORMAL, " ---[ This attack is obsolete, try hardnested instead ]---"); PrintAndLogEx(NORMAL, "Options:"); PrintAndLogEx(NORMAL, " h this help"); PrintAndLogEx(NORMAL, " target block number"); PrintAndLogEx(NORMAL, " target key type"); PrintAndLogEx(NORMAL, " candidate key from mf_nonce_brute tool"); PrintAndLogEx(NORMAL, "Examples:"); PrintAndLogEx(NORMAL, _YELLOW_(" hf mf keybrute --blk 1 -k 000011223344")); return 0; } */ int mfc_ev1_print_signature(uint8_t *uid, uint8_t uidlen, uint8_t *signature, int signature_len) { // ref: MIFARE Classic EV1 Originality Signature Validation #define PUBLIC_MFCEV1_ECDA_KEYLEN 33 const ecdsa_publickey_t nxp_mfc_public_keys[] = { {"NXP Mifare Classic MFC1C14_x", "044F6D3F294DEA5737F0F46FFEE88A356EED95695DD7E0C27A591E6F6F65962BAF"}, }; uint8_t i; bool is_valid = false; for (i = 0; i < ARRAYLEN(nxp_mfc_public_keys); i++) { int dl = 0; uint8_t key[PUBLIC_MFCEV1_ECDA_KEYLEN]; param_gethex_to_eol(nxp_mfc_public_keys[i].value, 0, key, PUBLIC_MFCEV1_ECDA_KEYLEN, &dl); int res = ecdsa_signature_r_s_verify(MBEDTLS_ECP_DP_SECP128R1, key, uid, uidlen, signature, signature_len, false); is_valid = (res == 0); if (is_valid) break; } PrintAndLogEx(INFO, ""); PrintAndLogEx(INFO, "--- " _CYAN_("Tag Signature")); if (is_valid == false || i == ARRAYLEN(nxp_mfc_public_keys)) { PrintAndLogEx(INFO, " Elliptic curve parameters: NID_secp128r1"); PrintAndLogEx(INFO, " TAG IC Signature: %s", sprint_hex_inrow(signature, 32)); PrintAndLogEx(SUCCESS, " Signature verification: " _RED_("failed")); return PM3_ESOFT; } PrintAndLogEx(INFO, " IC signature public key name: %s", nxp_mfc_public_keys[i].desc); PrintAndLogEx(INFO, "IC signature public key value: %s", nxp_mfc_public_keys[i].value); PrintAndLogEx(INFO, " Elliptic curve parameters: NID_secp128r1"); PrintAndLogEx(INFO, " TAG IC Signature: %s", sprint_hex_inrow(signature, 32)); PrintAndLogEx(SUCCESS, " Signature verification: " _GREEN_("successful")); return PM3_SUCCESS; } static int GetHFMF14AUID(uint8_t *uid, int *uidlen) { clearCommandBuffer(); SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT, 0, 0, NULL, 0); PacketResponseNG resp; if (!WaitForResponseTimeout(CMD_ACK, &resp, 2500)) { PrintAndLogEx(WARNING, "iso14443a card select failed"); DropField(); return 0; } iso14a_card_select_t card; memcpy(&card, (iso14a_card_select_t *)resp.data.asBytes, sizeof(iso14a_card_select_t)); memcpy(uid, card.uid, card.uidlen * sizeof(uint8_t)); *uidlen = card.uidlen; return 1; } static char *GenerateFilename(const char *prefix, const char *suffix) { if (! IfPm3Iso14443a()) { return NULL; } uint8_t uid[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; int uidlen = 0; char *fptr = calloc(sizeof(char) * (strlen(prefix) + strlen(suffix)) + sizeof(uid) * 2 + 1, sizeof(uint8_t)); GetHFMF14AUID(uid, &uidlen); if (!uidlen) { PrintAndLogEx(WARNING, "No tag found."); free(fptr); return NULL; } strcpy(fptr, prefix); FillFileNameByUID(fptr, uid, suffix, uidlen); return fptr; } static int32_t initSectorTable(sector_t **src, int32_t items) { (*src) = calloc(items, sizeof(sector_t)); if (*src == NULL) return -1; // empty e_sector for (int i = 0; i < items; ++i) { for (int j = 0; j < 2; ++j) { (*src)[i].Key[j] = 0xffffffffffff; (*src)[i].foundKey[j] = false; } } return items; } static void decode_print_st(uint16_t blockno, uint8_t *data) { if (mfIsSectorTrailer(blockno)) { PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "----------------------- " _CYAN_("Sector trailer decoder") " -----------------------"); PrintAndLogEx(INFO, "key A........ " _GREEN_("%s"), sprint_hex_inrow(data, 6)); PrintAndLogEx(INFO, "acr.......... " _GREEN_("%s"), sprint_hex_inrow(data + 6, 3)); PrintAndLogEx(INFO, "user / gdb... " _GREEN_("%02x"), data[9]); PrintAndLogEx(INFO, "key B........ " _GREEN_("%s"), sprint_hex_inrow(data + 10, 6)); PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, " # | Access rights"); PrintAndLogEx(INFO, "----+-----------------------------------------------------------------"); int bln = mfFirstBlockOfSector(mfSectorNum(blockno)); int blinc = (mfNumBlocksPerSector(mfSectorNum(blockno)) > 4) ? 5 : 1; for (int i = 0; i < 4; i++) { PrintAndLogEx(INFO, "%3d%c| " _YELLOW_("%s"), bln, ((blinc > 1) && (i < 3) ? '+' : ' '), mfGetAccessConditionsDesc(i, &data[6])); bln += blinc; } PrintAndLogEx(INFO, "----------------------------------------------------------------------"); PrintAndLogEx(NORMAL, ""); } } static uint8_t NumOfSectors(char card) { switch (card) { case '0' : return MIFARE_MINI_MAXSECTOR; case '1' : return MIFARE_1K_MAXSECTOR; case '2' : return MIFARE_2K_MAXSECTOR; case '4' : return MIFARE_4K_MAXSECTOR; default : return 0; } } static uint8_t FirstBlockOfSector(uint8_t sectorNo) { if (sectorNo < 32) { return sectorNo * 4; } else { return 32 * 4 + (sectorNo - 32) * 16; } } static uint8_t NumBlocksPerSector(uint8_t sectorNo) { if (sectorNo < 32) { return 4; } else { return 16; } } static uint8_t GetSectorFromBlockNo(uint8_t blockNo) { if (blockNo < 128) return blockNo / 4; else return 32 + ((128 - blockNo) / 16); } static char GetFormatFromSector(uint8_t sectorNo) { switch (sectorNo) { case MIFARE_MINI_MAXSECTOR: return '0'; case MIFARE_1K_MAXSECTOR: return '1'; case MIFARE_2K_MAXSECTOR: return '2'; case MIFARE_4K_MAXSECTOR: return '4'; default : return ' '; } } static void mf_print_block(uint8_t blockno, uint8_t *d) { if (blockno == 0) { PrintAndLogEx(INFO, "%3d | " _RED_("%s"), blockno, sprint_hex_ascii(d, MFBLOCK_SIZE)); } else if (mfIsSectorTrailer(blockno)) { PrintAndLogEx(INFO, "%3d | " _YELLOW_("%s"), blockno, sprint_hex_ascii(d, MFBLOCK_SIZE)); } else { PrintAndLogEx(INFO, "%3d | %s ", blockno, sprint_hex_ascii(d, MFBLOCK_SIZE)); } } static void mf_print_blocks(uint16_t n, uint8_t *d) { PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "----+-------------------------------------------------+-----------------"); PrintAndLogEx(INFO, "blk | data | ascii"); PrintAndLogEx(INFO, "----+-------------------------------------------------+-----------------"); for (uint16_t i = 0; i < n; i++) { mf_print_block(i, d + (i * MFBLOCK_SIZE)); } PrintAndLogEx(INFO, "----+-------------------------------------------------+-----------------"); PrintAndLogEx(NORMAL, ""); } static void mf_print_sector_hdr(uint8_t sector) { PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, " # | sector " _GREEN_("%02d") " / " _GREEN_("0x%02X") " | ascii", sector, sector); PrintAndLogEx(INFO, "----+-------------------------------------------------+-----------------"); } static int CmdHF14AMfDarkside(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mf darkside", "Darkside attack", "hf mf darkside\n" "hf mf darkside --blk 16\n" "hf mf darkside --blk 16 -b\n"); void *argtable[] = { arg_param_begin, arg_int0(NULL, "blk", " ", "Simulation type to use"), arg_lit0("b", NULL, "Target key B instead of default key A"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); uint8_t blockno = arg_get_u32_def(ctx, 1, 0); uint8_t key_type = MIFARE_AUTH_KEYA; if (arg_get_lit(ctx, 2)) { PrintAndLogEx(INFO, "Targeting key B"); key_type = MIFARE_AUTH_KEYB; } CLIParserFree(ctx); uint64_t key = 0; int isOK = mfDarkside(blockno, key_type, &key); PrintAndLogEx(NORMAL, ""); switch (isOK) { case -1 : PrintAndLogEx(WARNING, "button pressed. Aborted."); return PM3_ESOFT; case -2 : PrintAndLogEx(FAILED, "card is not vulnerable to Darkside attack (doesn't send NACK on authentication requests)."); return PM3_ESOFT; case -3 : PrintAndLogEx(FAILED, "card is not vulnerable to Darkside attack (its random number generator is not predictable)."); return PM3_ESOFT; case -4 : PrintAndLogEx(FAILED, "card is not vulnerable to Darkside attack (its random number generator seems to be based on the wellknown"); PrintAndLogEx(FAILED, "generating polynomial with 16 effective bits only, but shows unexpected behaviour."); return PM3_ESOFT; case -5 : PrintAndLogEx(WARNING, "aborted via keyboard."); return PM3_ESOFT; default : PrintAndLogEx(SUCCESS, "found valid key: "_YELLOW_("%012" PRIx64), key); break; } PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; } static int CmdHF14AMfWrBl(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mf wrbl", "Write MIFARE Classic block", "hf mf wrbl --blk 1 -k FFFFFFFFFFFF -d 000102030405060708090a0b0c0d0e0f" ); void *argtable[] = { arg_param_begin, arg_int1(NULL, "blk", "", "block number"), arg_lit0("a", NULL, "input key type is key A (def)"), arg_lit0("b", NULL, "input key type is key B"), arg_str0("k", "key", "", "key, 6 hex bytes"), arg_str0("d", "data", "", "bytes to write, 16 hex bytes"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); int b = arg_get_int_def(ctx, 1, 0); uint8_t keytype = MF_KEY_A; if (arg_get_lit(ctx, 2) && arg_get_lit(ctx, 3)) { CLIParserFree(ctx); PrintAndLogEx(WARNING, "Input key type must be A or B"); return PM3_EINVARG; } else if (arg_get_lit(ctx, 3)) { keytype = MF_KEY_B;; } int keylen = 0; uint8_t key[6] = {0}; CLIGetHexWithReturn(ctx, 4, key, &keylen); uint8_t block[MFBLOCK_SIZE] = {0x00}; int blen = 0; CLIGetHexWithReturn(ctx, 5, block, &blen); CLIParserFree(ctx); if (blen != MFBLOCK_SIZE) { PrintAndLogEx(WARNING, "block data must include 16 HEX bytes. Got %i", blen); return PM3_EINVARG; } if (b > 255) { return PM3_EINVARG; } uint8_t blockno = (uint8_t)b; PrintAndLogEx(INFO, "--block no %d, key %c - %s", blockno, (keytype == MF_KEY_B) ? 'B' : 'A', sprint_hex_inrow(key, sizeof(key))); PrintAndLogEx(INFO, "--data: %s", sprint_hex(block, sizeof(block))); uint8_t data[26]; memcpy(data, key, sizeof(key)); memcpy(data + 10, block, sizeof(block)); clearCommandBuffer(); SendCommandMIX(CMD_HF_MIFARE_WRITEBL, blockno, keytype, 0, data, sizeof(data)); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) { PrintAndLogEx(FAILED, "Command execute timeout"); return PM3_ETIMEOUT; } uint8_t isok = resp.oldarg[0] & 0xff; if (isok) { PrintAndLogEx(SUCCESS, "Write ( " _GREEN_("ok") " )"); PrintAndLogEx(HINT, "try `" _YELLOW_("hf mf rdbl") "` to verify"); } else { PrintAndLogEx(FAILED, "Write ( " _RED_("fail") " )"); // suggest the opposite keytype than what was used. PrintAndLogEx(HINT, "Maybe access rights? Try specify keytype " _YELLOW_("%c") " instead", (keytype == MF_KEY_A) ? 'B' : 'A'); } return PM3_SUCCESS; } static int CmdHF14AMfRdBl(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mf rdbl", "Read MIFARE Classic block", "hf mf rdbl --blk 0 -k FFFFFFFFFFFF\n" "hf mf rdbl -b 3 -v -> get block 3, decode sector trailer\n" ); void *argtable[] = { arg_param_begin, arg_int1(NULL, "blk", "", "block number"), arg_lit0("a", NULL, "input key type is key A (def)"), arg_lit0("b", NULL, "input key type is key B"), arg_str0("k", "key", "", "key, 6 hex bytes"), arg_lit0("v", "verbose", "verbose output"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); int b = arg_get_int_def(ctx, 1, 0); uint8_t keytype = MF_KEY_A; if (arg_get_lit(ctx, 2) && arg_get_lit(ctx, 3)) { CLIParserFree(ctx); PrintAndLogEx(WARNING, "Input key type must be A or B"); return PM3_EINVARG; } else if (arg_get_lit(ctx, 3)) { keytype = MF_KEY_B; } int keylen = 0; uint8_t key[6] = {0}; CLIGetHexWithReturn(ctx, 4, key, &keylen); bool verbose = arg_get_lit(ctx, 5); CLIParserFree(ctx); if (b > 255) { return PM3_EINVARG; } uint8_t blockno = (uint8_t)b; uint8_t data[16] = {0}; int res = mfReadBlock(blockno, keytype, key, data); if (res == PM3_SUCCESS) { uint8_t sector = GetSectorFromBlockNo(blockno); mf_print_sector_hdr(sector); mf_print_block(blockno, data); if (verbose) { decode_print_st(blockno, data); } } PrintAndLogEx(NORMAL, ""); return res; } static int CmdHF14AMfRdSc(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mf rdsc", "Read MIFARE Classic sector", "hf mf rdsc -s 0 -k FFFFFFFFFFFF\n" ); void *argtable[] = { arg_param_begin, arg_lit0("a", NULL, "input key specified is A key (def)"), arg_lit0("b", NULL, "input key specified is B key"), arg_str0("k", "key", "", "key specified as 6 hex bytes"), arg_int1("s", "sec", "", "sector number"), arg_lit0("v", "verbose", "verbose output"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); uint8_t keytype = MF_KEY_A; if (arg_get_lit(ctx, 1) && arg_get_lit(ctx, 2)) { CLIParserFree(ctx); PrintAndLogEx(WARNING, "Input key type must be A or B"); return PM3_EINVARG; } else if (arg_get_lit(ctx, 2)) { keytype = MF_KEY_B; } int keylen = 0; uint8_t key[6] = {0}; CLIGetHexWithReturn(ctx, 3, key, &keylen); int s = arg_get_int_def(ctx, 4, 0); bool verbose = arg_get_lit(ctx, 5); CLIParserFree(ctx); if (s > MIFARE_4K_MAXSECTOR) { PrintAndLogEx(WARNING, "Sector number must be less then 40"); return PM3_EINVARG; } uint8_t sector = (uint8_t)s; uint8_t sc_size = mfNumBlocksPerSector(sector) * MFBLOCK_SIZE; uint8_t *data = calloc(sc_size, sizeof(uint8_t)); if (data == NULL) { PrintAndLogEx(ERR, "failed to allocate memory"); return PM3_EMALLOC; } int res = mfReadSector(sector, keytype, key, data); if (res == PM3_SUCCESS) { uint8_t blocks = NumBlocksPerSector(sector); uint8_t start = FirstBlockOfSector(sector); mf_print_sector_hdr(sector); for (int i = 0; i < blocks; i++) { mf_print_block(start + i, data + (i * MFBLOCK_SIZE)); } if (verbose) { decode_print_st(start + blocks - 1, data + ((blocks - 1) * MFBLOCK_SIZE)); } } free(data); PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; } static int FastDumpWithEcFill(uint8_t numsectors) { PacketResponseNG resp; mfc_eload_t payload; payload.sectorcnt = numsectors; payload.keytype = MF_KEY_A; // ecfill key A clearCommandBuffer(); SendCommandNG(CMD_HF_MIFARE_EML_LOAD, (uint8_t *)&payload, sizeof(payload)); bool res = WaitForResponseTimeout(CMD_HF_MIFARE_EML_LOAD, &resp, 2500); if (res == false) { PrintAndLogEx(WARNING, "Command execute timeout"); return PM3_ETIMEOUT; } if (resp.status != PM3_SUCCESS) { PrintAndLogEx(INFO, "fast dump reported back failure w KEY A, swapping to KEY B"); // ecfill key B payload.keytype = MF_KEY_B; clearCommandBuffer(); SendCommandNG(CMD_HF_MIFARE_EML_LOAD, (uint8_t *)&payload, sizeof(payload)); res = WaitForResponseTimeout(CMD_HF_MIFARE_EML_LOAD, &resp, 2500); if (res == false) { PrintAndLogEx(WARNING, "Command execute timeout"); return PM3_ETIMEOUT; } if (resp.status != PM3_SUCCESS) { PrintAndLogEx(INFO, "fast dump reported back failure w KEY B"); PrintAndLogEx(INFO, "Dump file is " _RED_("PARTIAL") " complete"); } } 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; } 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; if (keyFilename[0] == 0x00) { fptr = GenerateFilename("hf-mf-", "-key.bin"); if (fptr == NULL) return PM3_ESOFT; strcpy(keyFilename, fptr); free(fptr); } if ((f = fopen(keyFilename, "rb")) == NULL) { PrintAndLogEx(WARNING, "Could not find file " _YELLOW_("%s"), keyFilename); return PM3_EFILE; } PrintAndLogEx(INFO, "Using `" _YELLOW_("%s") "`", keyFilename); // Read keys A from file size_t bytes_read; for (sectorNo = 0; sectorNo < numSectors; sectorNo++) { bytes_read = fread(keyA[sectorNo], 1, 6, f); if (bytes_read != 6) { PrintAndLogEx(ERR, "File reading error."); fclose(f); return PM3_EFILE; } } // Read keys B from file for (sectorNo = 0; sectorNo < numSectors; sectorNo++) { bytes_read = fread(keyB[sectorNo], 1, 6, f); if (bytes_read != 6) { PrintAndLogEx(ERR, "File reading error."); fclose(f); return PM3_EFILE; } } fclose(f); PrintAndLogEx(INFO, "Reading sector access bits..."); PrintAndLogEx(INFO, "." NOLF); uint8_t tries; mf_readblock_t payload; for (sectorNo = 0; sectorNo < numSectors; sectorNo++) { for (tries = 0; tries < MIFARE_SECTOR_RETRY; tries++) { PrintAndLogEx(NORMAL, "." NOLF); fflush(stdout); payload.blockno = FirstBlockOfSector(sectorNo) + NumBlocksPerSector(sectorNo) - 1; payload.keytype = MF_KEY_A; memcpy(payload.key, keyA[sectorNo], sizeof(payload.key)); clearCommandBuffer(); SendCommandNG(CMD_HF_MIFARE_READBL, (uint8_t *)&payload, sizeof(mf_readblock_t)); if (WaitForResponseTimeout(CMD_HF_MIFARE_READBL, &resp, 1500)) { uint8_t *data = resp.data.asBytes; if (resp.status == PM3_SUCCESS) { rights[sectorNo][0] = ((data[7] & 0x10) >> 2) | ((data[8] & 0x1) << 1) | ((data[8] & 0x10) >> 4); // C1C2C3 for data area 0 rights[sectorNo][1] = ((data[7] & 0x20) >> 3) | ((data[8] & 0x2) << 0) | ((data[8] & 0x20) >> 5); // C1C2C3 for data area 1 rights[sectorNo][2] = ((data[7] & 0x40) >> 4) | ((data[8] & 0x4) >> 1) | ((data[8] & 0x40) >> 6); // C1C2C3 for data area 2 rights[sectorNo][3] = ((data[7] & 0x80) >> 5) | ((data[8] & 0x8) >> 2) | ((data[8] & 0x80) >> 7); // C1C2C3 for sector trailer break; } else if (tries == 2) { // on last try set defaults PrintAndLogEx(FAILED, "\ncould not get access rights for sector %2d. Trying with defaults...", sectorNo); rights[sectorNo][0] = rights[sectorNo][1] = rights[sectorNo][2] = 0x00; rights[sectorNo][3] = 0x01; } } else { PrintAndLogEx(FAILED, "\ncommand execute timeout when trying to read access rights for sector %2d. Trying with defaults...", sectorNo); rights[sectorNo][0] = rights[sectorNo][1] = rights[sectorNo][2] = 0x00; rights[sectorNo][3] = 0x01; } } } PrintAndLogEx(NORMAL, ""); PrintAndLogEx(SUCCESS, "Finished reading sector access bits"); PrintAndLogEx(INFO, "Dumping all blocks from card..."); for (sectorNo = 0; sectorNo < numSectors; sectorNo++) { for (blockNo = 0; blockNo < NumBlocksPerSector(sectorNo); blockNo++) { bool received = false; for (tries = 0; tries < MIFARE_SECTOR_RETRY; tries++) { if (blockNo == NumBlocksPerSector(sectorNo) - 1) { // sector trailer. At least the Access Conditions can always be read with key A. payload.blockno = FirstBlockOfSector(sectorNo) + blockNo; payload.keytype = MF_KEY_A; memcpy(payload.key, keyA[sectorNo], sizeof(payload.key)); clearCommandBuffer(); SendCommandNG(CMD_HF_MIFARE_READBL, (uint8_t *)&payload, sizeof(mf_readblock_t)); received = WaitForResponseTimeout(CMD_HF_MIFARE_READBL, &resp, 1500); } else { // data block. Check if it can be read with key A or key B uint8_t data_area = (sectorNo < 32) ? blockNo : blockNo / 5; if ((rights[sectorNo][data_area] == 0x03) || (rights[sectorNo][data_area] == 0x05)) { // only key B would work payload.blockno = FirstBlockOfSector(sectorNo) + blockNo; payload.keytype = 1; memcpy(payload.key, keyB[sectorNo], sizeof(payload.key)); clearCommandBuffer(); SendCommandNG(CMD_HF_MIFARE_READBL, (uint8_t *)&payload, sizeof(mf_readblock_t)); received = WaitForResponseTimeout(CMD_HF_MIFARE_READBL, &resp, 1500); } else if (rights[sectorNo][data_area] == 0x07) { // no key would work PrintAndLogEx(WARNING, "access rights do not allow reading of sector %2d block %3d", sectorNo, blockNo); // where do you want to go?? Next sector or block? break; } else { // key A would work payload.blockno = FirstBlockOfSector(sectorNo) + blockNo; payload.keytype = MF_KEY_A; memcpy(payload.key, keyA[sectorNo], sizeof(payload.key)); clearCommandBuffer(); SendCommandNG(CMD_HF_MIFARE_READBL, (uint8_t *)&payload, sizeof(mf_readblock_t)); received = WaitForResponseTimeout(CMD_HF_MIFARE_READBL, &resp, 1500); } } if (received) { if (resp.status == PM3_SUCCESS) { // break the re-try loop break; } } } if (received) { uint8_t *data = resp.data.asBytes; if (blockNo == NumBlocksPerSector(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]); } if (resp.status == PM3_SUCCESS) { memcpy(carddata[FirstBlockOfSector(sectorNo) + blockNo], data, 16); PrintAndLogEx(SUCCESS, "successfully read block %2d of sector %2d.", blockNo, sectorNo); } else { PrintAndLogEx(FAILED, "could not read block %2d of sector %2d", blockNo, sectorNo); break; } } else { PrintAndLogEx(WARNING, "command execute timeout when trying to read block %2d of sector %2d.", blockNo, sectorNo); break; } } } PrintAndLogEx(SUCCESS, "time: %" PRIu64 " seconds\n", (msclock() - t1) / 1000); PrintAndLogEx(SUCCESS, "\nSucceeded in dumping all blocks"); if (strlen(dataFilename) < 1) { fptr = GenerateFilename("hf-mf-", "-dump"); if (fptr == NULL) return PM3_ESOFT; strcpy(dataFilename, fptr); free(fptr); } uint16_t bytes = 16 * (FirstBlockOfSector(numSectors - 1) + NumBlocksPerSector(numSectors - 1)); saveFile(dataFilename, ".bin", (uint8_t *)carddata, bytes); saveFileEML(dataFilename, (uint8_t *)carddata, bytes, MFBLOCK_SIZE); saveFileJSON(dataFilename, jsfCardMemory, (uint8_t *)carddata, bytes, NULL); return PM3_SUCCESS; } static int CmdHF14AMfRestore(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mf restore", "Restore MIFARE Classic binary file to tag.\n" "\n" "The key file and data file will program the card sector trailers.\n" "By default we authenticate to card with key B 0xFFFFFFFFFFFF.\n" "\n" "`--uid` param is used for filename templates `hf-mf--dump.bin` and `hf-mf--key.bin.\n" " If not specified, it will read the card uid instead.\n" " `-w` param you can indicate that the key file should be used for authentication instead.\n" " if so we also try both B/A keys", "hf mf restore\n" "hf mf restore --1k --uid 04010203\n" "hf mf restore --1k --uid 04010203 -k hf-mf-AABBCCDD-key.bin\n" "hf mf restore --4k" ); void *argtable[] = { arg_param_begin, 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_str0("u", "uid", "", "uid, 6 hex bytes"), arg_str0("f", "file", "", "data filename"), arg_str0("k", "kfn", "", "key filename"), arg_lit0(NULL, "ka", "use specified keyfile to authenticate"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); bool m0 = arg_get_lit(ctx, 1); bool m1 = arg_get_lit(ctx, 2); bool m2 = arg_get_lit(ctx, 3); bool m4 = arg_get_lit(ctx, 4); int uidlen = 0; char uid[14] = {0}; CLIParamStrToBuf(arg_get_str(ctx, 5), (uint8_t *)uid, sizeof(uid), &uidlen); int datafnlen = 0; char datafilename[FILE_PATH_SIZE] = {0}; CLIParamStrToBuf(arg_get_str(ctx, 6), (uint8_t *)datafilename, FILE_PATH_SIZE, &datafnlen); int keyfnlen = 0; char keyfilename[FILE_PATH_SIZE] = {0}; CLIParamStrToBuf(arg_get_str(ctx, 7), (uint8_t *)keyfilename, FILE_PATH_SIZE, &keyfnlen); bool use_keyfile_for_auth = arg_get_lit(ctx, 8); CLIParserFree(ctx); // 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 sectors = MIFARE_1K_MAXSECTOR; if (m0) { sectors = MIFARE_MINI_MAXSECTOR; } else if (m1) { sectors = MIFARE_1K_MAXSECTOR; } else if (m2) { sectors = MIFARE_2K_MAXSECTOR; } else if (m4) { sectors = MIFARE_4K_MAXSECTOR; } else { PrintAndLogEx(WARNING, "Please specify a MIFARE Type"); return PM3_EINVARG; } // if user specified UID, use it in file templates if (uidlen) { if (keyfnlen == 0) { snprintf(keyfilename, FILE_PATH_SIZE, "hf-mf-%s-key.bin", uid); keyfnlen = strlen(keyfilename); } if (datafnlen == 0) { snprintf(datafilename, FILE_PATH_SIZE, "hf-mf-%s-dump.bin", uid); datafnlen = strlen(datafilename); } } // try reading card uid and create filename if (keyfnlen == 0) { char *fptr = GenerateFilename("hf-mf-", "-key.bin"); if (fptr == NULL) return PM3_ESOFT; strcpy(keyfilename, fptr); free(fptr); } FILE *f; if ((f = fopen(keyfilename, "rb")) == NULL) { PrintAndLogEx(WARNING, "Could not find file " _YELLOW_("%s"), keyfilename); return PM3_EFILE; } // key arrays uint8_t keyA[40][6]; uint8_t keyB[40][6]; // read key file size_t bytes_read; for (uint8_t s = 0; s < sectors; s++) { bytes_read = fread(keyA[s], 1, 6, f); if (bytes_read != 6) { PrintAndLogEx(ERR, "File reading error " _YELLOW_("%s"), keyfilename); fclose(f); return PM3_EFILE; } } for (uint8_t s = 0; s < sectors; s++) { bytes_read = fread(keyB[s], 1, 6, f); if (bytes_read != 6) { PrintAndLogEx(ERR, "File reading error " _YELLOW_("%s"), keyfilename); fclose(f); return PM3_EFILE; } } fclose(f); f = NULL; // try reading card uid and create filename if (datafnlen == 0) { char *fptr = GenerateFilename("hf-mf-", "-dump.bin"); if (fptr == NULL) return PM3_ESOFT; strcpy(datafilename, fptr); free(fptr); } // read dump file if ((f = fopen(datafilename, "rb")) == NULL) { PrintAndLogEx(WARNING, "Could not find file " _YELLOW_("%s"), datafilename); return PM3_EINVARG; } // default authentication key uint8_t default_key[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; PrintAndLogEx(INFO, "Restoring " _YELLOW_("%s")" to card", datafilename); for (uint8_t s = 0; s < sectors; s++) { for (uint8_t b = 0; b < NumBlocksPerSector(s); b++) { uint8_t data[26]; uint8_t bldata[MFBLOCK_SIZE] = {0x00}; bytes_read = fread(bldata, 1, MFBLOCK_SIZE, f); if (bytes_read != sizeof(bldata)) { PrintAndLogEx(ERR, "File reading error " _YELLOW_("%s"), datafilename); fclose(f); f = NULL; return PM3_EFILE; } if (use_keyfile_for_auth == false) { // sector trailer if (b == NumBlocksPerSector(s) - 1) { bldata[0] = (keyA[s][0]); bldata[1] = (keyA[s][1]); bldata[2] = (keyA[s][2]); bldata[3] = (keyA[s][3]); bldata[4] = (keyA[s][4]); bldata[5] = (keyA[s][5]); bldata[10] = (keyB[s][0]); bldata[11] = (keyB[s][1]); bldata[12] = (keyB[s][2]); bldata[13] = (keyB[s][3]); bldata[14] = (keyB[s][4]); bldata[15] = (keyB[s][5]); } } memcpy(data + 10, bldata, sizeof(bldata)); if (use_keyfile_for_auth) { for (int8_t kt = MF_KEY_B; kt > -1; kt--) { if (kt == MF_KEY_A) memcpy(data, keyA[s], 6); else memcpy(data, keyB[s], 6); PrintAndLogEx(INFO, "block %3d: %s", FirstBlockOfSector(s) + b, sprint_hex(bldata, sizeof(bldata))); clearCommandBuffer(); SendCommandMIX(CMD_HF_MIFARE_WRITEBL, FirstBlockOfSector(s) + b, kt, 0, data, sizeof(data)); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { uint8_t isOK = resp.oldarg[0] & 0xff; if (isOK == 0) { if (b == 0) { PrintAndLogEx(INFO, "Writing to manufacture block w key %c ( " _RED_("fail") " )", (kt == MF_KEY_A) ? 'A' : 'B'); } else { PrintAndLogEx(FAILED, "Write to block %u w key %c ( " _RED_("fail") " ) ", b, (kt == MF_KEY_A) ? 'A' : 'B'); } } else { // if success, skip to next block break; } } else { PrintAndLogEx(WARNING, "Command execute timeout"); } } } else { memcpy(data, default_key, 6); PrintAndLogEx(INFO, "block %3d: %s", FirstBlockOfSector(s) + b, sprint_hex(bldata, sizeof(bldata))); clearCommandBuffer(); SendCommandMIX(CMD_HF_MIFARE_WRITEBL, FirstBlockOfSector(s) + b, MF_KEY_B, 0, data, sizeof(data)); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { uint8_t isOK = resp.oldarg[0] & 0xff; if (isOK == 0) { if (b == 0) { PrintAndLogEx(INFO, "Writing to manufacture block w key B ( " _RED_("fail") " )"); } else { PrintAndLogEx(FAILED, "Write to block %u w key B ( " _RED_("fail") " )", b); } } } else { PrintAndLogEx(WARNING, "Command execute timeout"); } } } } fclose(f); PrintAndLogEx(INFO, "Done!"); return PM3_SUCCESS; } static int CmdHF14AMfNested(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mf nested", "Execute Nested attack against MIFARE Classic card for key recovery", "hf mf nested --single --blk 0 -a FFFFFFFFFFFF --tblk 4 --ta --> Single sector key recovery. Use block 0 Key A to find block 4 Key A\n" "hf mf nested --mini --blk 0 -a -k FFFFFFFFFFFF --> Key recovery against MIFARE Mini\n" "hf mf nested --1k --blk 0 -a -k FFFFFFFFFFFF --> Key recovery against MIFARE Classic 1k\n" "hf mf nested --2k --blk 0 -a -k FFFFFFFFFFFF --> Key recovery against MIFARE 2k\n" "hf mf nested --4k --blk 0 -a -k FFFFFFFFFFFF --> Key recovery against MIFARE 4k"); void *argtable[] = { arg_param_begin, arg_str0("k", "key", "", "Key specified as 12 hex symbols"), arg_lit0(NULL, "mini", "MIFARE Classic Mini / S20"), arg_lit0(NULL, "1k", "MIFARE Classic 1k / S50"), arg_lit0(NULL, "2k", "MIFARE Classic/Plus 2k"), arg_lit0(NULL, "4k", "MIFARE Classic 4k / S70"), arg_int0(NULL, "blk", "", "Input block number"), arg_lit0("a", NULL, "Input key specified is A key (default)"), arg_lit0("b", NULL, "Input key specified is B key"), arg_int0(NULL, "tblk", "", "Target block number"), arg_lit0(NULL, "ta", "Target A key (default)"), arg_lit0(NULL, "tb", "Target B key"), arg_lit0(NULL, "emu", "Fill simulator keys from found keys"), arg_lit0(NULL, "dump", "Dump found keys to file"), arg_lit0(NULL, "single", "Single sector (defaults to All)"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); int keylen = 0; uint8_t key[6] = {0}; CLIGetHexWithReturn(ctx, 1, key, &keylen); bool m0 = arg_get_lit(ctx, 2); bool m1 = arg_get_lit(ctx, 3); bool m2 = arg_get_lit(ctx, 4); bool m4 = arg_get_lit(ctx, 5); uint8_t blockNo = arg_get_u32_def(ctx, 6, 0); uint8_t keyType = MF_KEY_A; if (arg_get_lit(ctx, 7) && arg_get_lit(ctx, 8)) { CLIParserFree(ctx); PrintAndLogEx(WARNING, "Input key type must be A or B"); return PM3_EINVARG; } else if (arg_get_lit(ctx, 8)) { keyType = MF_KEY_B; } uint8_t trgBlockNo = arg_get_u32_def(ctx, 9, 0); uint8_t trgKeyType = MF_KEY_A; if (arg_get_lit(ctx, 10) && arg_get_lit(ctx, 11)) { CLIParserFree(ctx); PrintAndLogEx(WARNING, "Target key type must be A or B"); return PM3_EINVARG; } else if (arg_get_lit(ctx, 11)) { trgKeyType = MF_KEY_B; } bool transferToEml = arg_get_lit(ctx, 12); bool createDumpFile = arg_get_lit(ctx, 13); bool singleSector = arg_get_lit(ctx, 14); CLIParserFree(ctx); //validations if ((m0 + m1 + m2 + m4) > 1) { PrintAndLogEx(WARNING, "Only specify one MIFARE Type"); return PM3_EINVARG; } uint8_t SectorsCnt = 1; if (m0) { SectorsCnt = MIFARE_MINI_MAXSECTOR; } else if (m1) { SectorsCnt = MIFARE_1K_MAXSECTOR; } else if (m2) { SectorsCnt = MIFARE_2K_MAXSECTOR; } else if (m4) { SectorsCnt = MIFARE_4K_MAXSECTOR; } if (singleSector == false) { if (SectorsCnt == 0) { PrintAndLogEx(WARNING, "Invalid MIFARE Type"); return PM3_EINVARG; } } if (keylen != 6) { PrintAndLogEx(WARNING, "Input key must include 12 HEX symbols"); return PM3_EINVARG; } sector_t *e_sector = NULL; uint8_t keyBlock[(ARRAYLEN(g_mifare_default_keys) + 1) * 6]; uint64_t key64 = 0; // check if tag doesn't have static nonce if (detect_classic_static_nonce() == NONCE_STATIC) { PrintAndLogEx(WARNING, "Static nonce detected. Quitting..."); PrintAndLogEx(INFO, "\t Try use " _YELLOW_("`hf mf staticnested`")); return PM3_EOPABORTED; } // check if we can authenticate to sector if (mfCheckKeys(blockNo, keyType, true, 1, key, &key64) != PM3_SUCCESS) { PrintAndLogEx(WARNING, "Wrong key. Can't authenticate to block:%3d key type:%c", blockNo, keyType ? 'B' : 'A'); return PM3_EOPABORTED; } if (singleSector) { int16_t isOK = mfnested(blockNo, keyType, key, trgBlockNo, trgKeyType, keyBlock, true); switch (isOK) { case PM3_ETIMEOUT: PrintAndLogEx(ERR, "Command execute timeout\n"); break; case PM3_EOPABORTED: PrintAndLogEx(WARNING, "Button pressed. Aborted.\n"); break; case PM3_EFAILED: PrintAndLogEx(FAILED, "Tag isn't vulnerable to Nested Attack (PRNG is not predictable).\n"); break; case PM3_ESOFT: PrintAndLogEx(FAILED, "No valid key found"); break; case PM3_SUCCESS: key64 = bytes_to_num(keyBlock, 6); // transfer key to the emulator if (transferToEml) { uint8_t sectortrailer; if (trgBlockNo < 32 * 4) { // 4 block sector sectortrailer = trgBlockNo | 0x03; } else { // 16 block sector sectortrailer = trgBlockNo | 0x0f; } mfEmlGetMem(keyBlock, sectortrailer, 1); if (trgKeyType == MF_KEY_A) num_to_bytes(key64, 6, keyBlock); else num_to_bytes(key64, 6, &keyBlock[10]); mfEmlSetMem(keyBlock, sectortrailer, 1); PrintAndLogEx(SUCCESS, "Key transferred to emulator memory."); } return PM3_SUCCESS; default : PrintAndLogEx(ERR, "Unknown error.\n"); } return PM3_SUCCESS; } else { // ------------------------------------ multiple sectors working uint64_t t1 = msclock(); e_sector = calloc(SectorsCnt, sizeof(sector_t)); if (e_sector == NULL) return PM3_EMALLOC; // add our known key e_sector[GetSectorFromBlockNo(blockNo)].foundKey[keyType] = 1; e_sector[GetSectorFromBlockNo(blockNo)].Key[keyType] = key64; //test current key and additional standard keys first // add parameter key memcpy(keyBlock + (ARRAYLEN(g_mifare_default_keys) * 6), key, 6); for (int cnt = 0; cnt < ARRAYLEN(g_mifare_default_keys); cnt++) { num_to_bytes(g_mifare_default_keys[cnt], 6, (uint8_t *)(keyBlock + cnt * 6)); } PrintAndLogEx(SUCCESS, "Testing known keys. Sector count "_YELLOW_("%d"), SectorsCnt); int res = mfCheckKeys_fast(SectorsCnt, true, true, 1, ARRAYLEN(g_mifare_default_keys) + 1, keyBlock, e_sector, false); if (res == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, "Fast check found all keys"); goto jumptoend; } uint64_t t2 = msclock() - t1; PrintAndLogEx(SUCCESS, "Time to check " _YELLOW_("%zu") " known keys: %.0f seconds\n", ARRAYLEN(g_mifare_default_keys), (float)t2 / 1000.0); PrintAndLogEx(SUCCESS, "enter nested key recovery"); // nested sectors bool calibrate = true; for (trgKeyType = MF_KEY_A; trgKeyType <= MF_KEY_B; ++trgKeyType) { for (uint8_t sectorNo = 0; sectorNo < SectorsCnt; ++sectorNo) { for (int i = 0; i < MIFARE_SECTOR_RETRY; i++) { if (e_sector[sectorNo].foundKey[trgKeyType]) continue; int16_t isOK = mfnested(blockNo, keyType, key, FirstBlockOfSector(sectorNo), trgKeyType, keyBlock, calibrate); switch (isOK) { case PM3_ETIMEOUT: PrintAndLogEx(ERR, "Command execute timeout\n"); break; case PM3_EOPABORTED: PrintAndLogEx(WARNING, "button pressed. Aborted.\n"); break; case PM3_EFAILED : PrintAndLogEx(FAILED, "Tag isn't vulnerable to Nested Attack (PRNG is not predictable).\n"); break; case PM3_ESOFT: //key not found calibrate = false; continue; case PM3_SUCCESS: calibrate = false; e_sector[sectorNo].foundKey[trgKeyType] = 1; e_sector[sectorNo].Key[trgKeyType] = bytes_to_num(keyBlock, 6); mfCheckKeys_fast(SectorsCnt, true, true, 2, 1, keyBlock, e_sector, false); continue; default : PrintAndLogEx(ERR, "Unknown error.\n"); } free(e_sector); return PM3_ESOFT; } } } t1 = msclock() - t1; PrintAndLogEx(SUCCESS, "time in nested " _YELLOW_("%.0f") " seconds\n", (float)t1 / 1000.0); // 20160116 If Sector A is found, but not Sector B, try just reading it of the tag? PrintAndLogEx(INFO, "trying to read key B..."); for (int i = 0; i < SectorsCnt; i++) { // KEY A but not KEY B if (e_sector[i].foundKey[0] && !e_sector[i].foundKey[1]) { uint8_t sectrail = (FirstBlockOfSector(i) + NumBlocksPerSector(i) - 1); PrintAndLogEx(SUCCESS, "reading block %d", sectrail); mf_readblock_t payload; payload.blockno = sectrail; payload.keytype = MF_KEY_A; num_to_bytes(e_sector[i].Key[0], 6, payload.key); // KEY A clearCommandBuffer(); SendCommandNG(CMD_HF_MIFARE_READBL, (uint8_t *)&payload, sizeof(mf_readblock_t)); PacketResponseNG resp; if (!WaitForResponseTimeout(CMD_HF_MIFARE_READBL, &resp, 1500)) continue; if (resp.status != PM3_SUCCESS) continue; uint8_t *data = resp.data.asBytes; key64 = bytes_to_num(data + 10, 6); if (key64) { PrintAndLogEx(SUCCESS, "data: %s", sprint_hex(data + 10, 6)); e_sector[i].foundKey[1] = true; e_sector[i].Key[1] = key64; } } } jumptoend: PrintAndLogEx(NORMAL, ""); PrintAndLogEx(SUCCESS, _GREEN_("found keys:")); //print them printKeyTable(SectorsCnt, e_sector); // transfer them to the emulator if (transferToEml) { // fast push mode conn.block_after_ACK = true; for (int i = 0; i < SectorsCnt; i++) { mfEmlGetMem(keyBlock, FirstBlockOfSector(i) + NumBlocksPerSector(i) - 1, 1); if (e_sector[i].foundKey[0]) num_to_bytes(e_sector[i].Key[0], 6, keyBlock); if (e_sector[i].foundKey[1]) num_to_bytes(e_sector[i].Key[1], 6, &keyBlock[10]); if (i == SectorsCnt - 1) { // Disable fast mode on last packet conn.block_after_ACK = false; } mfEmlSetMem(keyBlock, FirstBlockOfSector(i) + NumBlocksPerSector(i) - 1, 1); } PrintAndLogEx(SUCCESS, "keys transferred to emulator memory."); } // Create dump file if (createDumpFile) { char *fptr = GenerateFilename("hf-mf-", "-key.bin"); if (createMfcKeyDump(fptr, SectorsCnt, e_sector) != PM3_SUCCESS) { PrintAndLogEx(ERR, "Failed to save keys to file"); free(e_sector); free(fptr); return PM3_ESOFT; } free(fptr); } free(e_sector); } return PM3_SUCCESS; } static int CmdHF14AMfNestedStatic(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mf staticnested", "Execute Nested attack against MIFARE Classic card with static nonce for key recovery", "hf mf staticnested --mini --blk 0 -a -k FFFFFFFFFFFF --> Key recovery against MIFARE Mini\n" "hf mf staticnested --1k --blk 0 -a -k FFFFFFFFFFFF --> Key recovery against MIFARE Classic 1k\n" "hf mf staticnested --2k --blk 0 -a -k FFFFFFFFFFFF --> Key recovery against MIFARE 2k\n" "hf mf staticnested --4k --blk 0 -a -k FFFFFFFFFFFF --> Key recovery against MIFARE 4k\n"); void *argtable[] = { arg_param_begin, arg_str0("k", "key", "", "Key specified as 12 hex symbols"), arg_lit0(NULL, "mini", "MIFARE Classic Mini / S20"), arg_lit0(NULL, "1k", "MIFARE Classic 1k / S50"), arg_lit0(NULL, "2k", "MIFARE Classic/Plus 2k"), arg_lit0(NULL, "4k", "MIFARE Classic 4k / S70"), arg_int0(NULL, "blk", "", "Input block number"), arg_lit0("a", NULL, "Input key specified is A key (default)"), arg_lit0("b", NULL, "Input key specified is B key"), arg_lit0("e", "emukeys", "Fill simulator keys from found keys"), arg_lit0(NULL, "dumpkeys", "Dump found keys to file"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); int keylen = 0; uint8_t key[6] = {0}; CLIGetHexWithReturn(ctx, 1, key, &keylen); bool m0 = arg_get_lit(ctx, 2); bool m1 = arg_get_lit(ctx, 3); bool m2 = arg_get_lit(ctx, 4); bool m4 = arg_get_lit(ctx, 5); uint8_t blockNo = arg_get_u32_def(ctx, 6, 0); uint8_t keyType = MF_KEY_A; if (arg_get_lit(ctx, 7) && arg_get_lit(ctx, 8)) { CLIParserFree(ctx); PrintAndLogEx(WARNING, "Input key type must be A or B"); return PM3_EINVARG; } else if (arg_get_lit(ctx, 8)) { keyType = MF_KEY_B; } bool transferToEml = arg_get_lit(ctx, 9); bool createDumpFile = arg_get_lit(ctx, 10); CLIParserFree(ctx); //validations if ((m0 + m1 + m2 + m4) > 1) { PrintAndLogEx(WARNING, "Only specify one MIFARE Type"); return PM3_EINVARG; } uint8_t SectorsCnt = 1; if (m0) { SectorsCnt = MIFARE_MINI_MAXSECTOR; } else if (m1) { SectorsCnt = MIFARE_1K_MAXSECTOR; } else if (m2) { SectorsCnt = MIFARE_2K_MAXSECTOR; } else if (m4) { SectorsCnt = MIFARE_4K_MAXSECTOR; } else { PrintAndLogEx(WARNING, "Please specify a MIFARE Type"); return PM3_EINVARG; } if (keylen != 6) { PrintAndLogEx(WARNING, "Input key must include 12 HEX symbols"); return PM3_EINVARG; } sector_t *e_sector = NULL; uint8_t trgKeyType = MF_KEY_A; uint8_t keyBlock[(ARRAYLEN(g_mifare_default_keys) + 1) * 6]; uint64_t key64 = 0; // check if tag have static nonce if (detect_classic_static_nonce() != NONCE_STATIC) { PrintAndLogEx(WARNING, "Normal nonce detected, or failed read of card. Quitting..."); PrintAndLogEx(INFO, "\t Try use " _YELLOW_("`hf mf nested`")); return PM3_EOPABORTED; } // check if we can authenticate to sector if (mfCheckKeys(blockNo, keyType, true, 1, key, &key64) != PM3_SUCCESS) { PrintAndLogEx(WARNING, "Wrong key. Can't authenticate to block: %3d key type: %c", blockNo, keyType ? 'B' : 'A'); return PM3_EOPABORTED; } if (IfPm3Flash()) { PrintAndLogEx(INFO, "RDV4 with flashmemory supported detected."); } uint64_t t1 = msclock(); e_sector = calloc(SectorsCnt, sizeof(sector_t)); if (e_sector == NULL) return PM3_EMALLOC; // add our known key e_sector[GetSectorFromBlockNo(blockNo)].foundKey[keyType] = 1; e_sector[GetSectorFromBlockNo(blockNo)].Key[keyType] = key64; //test current key and additional standard keys first // add parameter key memcpy(keyBlock + (ARRAYLEN(g_mifare_default_keys) * 6), key, 6); for (int cnt = 0; cnt < ARRAYLEN(g_mifare_default_keys); cnt++) { num_to_bytes(g_mifare_default_keys[cnt], 6, (uint8_t *)(keyBlock + cnt * 6)); } PrintAndLogEx(SUCCESS, "Testing known keys. Sector count "_YELLOW_("%d"), SectorsCnt); int res = mfCheckKeys_fast(SectorsCnt, true, true, 1, ARRAYLEN(g_mifare_default_keys) + 1, keyBlock, e_sector, false); if (res == PM3_SUCCESS) { // all keys found PrintAndLogEx(SUCCESS, "Fast check found all keys"); goto jumptoend; } uint64_t t2 = msclock() - t1; PrintAndLogEx(SUCCESS, "Time to check "_YELLOW_("%zu") " known keys: %.0f seconds\n", ARRAYLEN(g_mifare_default_keys), (float)t2 / 1000.0); PrintAndLogEx(SUCCESS, "enter static nested key recovery"); // nested sectors for (trgKeyType = MF_KEY_A; trgKeyType <= MF_KEY_B; ++trgKeyType) { for (uint8_t sectorNo = 0; sectorNo < SectorsCnt; ++sectorNo) { for (int i = 0; i < 1; i++) { if (e_sector[sectorNo].foundKey[trgKeyType]) continue; int16_t isOK = mfStaticNested(blockNo, keyType, key, FirstBlockOfSector(sectorNo), trgKeyType, keyBlock); switch (isOK) { case PM3_ETIMEOUT : PrintAndLogEx(ERR, "Command execute timeout"); break; case PM3_EOPABORTED : PrintAndLogEx(WARNING, "aborted via keyboard."); break; case PM3_ESOFT : continue; case PM3_SUCCESS : e_sector[sectorNo].foundKey[trgKeyType] = 1; e_sector[sectorNo].Key[trgKeyType] = bytes_to_num(keyBlock, 6); mfCheckKeys_fast(SectorsCnt, true, true, 2, 1, keyBlock, e_sector, false); continue; default : PrintAndLogEx(ERR, "unknown error.\n"); } free(e_sector); return PM3_ESOFT; } } } t1 = msclock() - t1; PrintAndLogEx(SUCCESS, "time in static nested " _YELLOW_("%.0f") " seconds\n", (float)t1 / 1000.0); // 20160116 If Sector A is found, but not Sector B, try just reading it of the tag? PrintAndLogEx(INFO, "trying to read key B..."); for (int i = 0; i < SectorsCnt; i++) { // KEY A but not KEY B if (e_sector[i].foundKey[0] && !e_sector[i].foundKey[1]) { uint8_t sectrail = (FirstBlockOfSector(i) + NumBlocksPerSector(i) - 1); PrintAndLogEx(SUCCESS, "reading block %d", sectrail); mf_readblock_t payload; payload.blockno = sectrail; payload.keytype = MF_KEY_A; num_to_bytes(e_sector[i].Key[0], 6, payload.key); // KEY A clearCommandBuffer(); SendCommandNG(CMD_HF_MIFARE_READBL, (uint8_t *)&payload, sizeof(mf_readblock_t)); PacketResponseNG resp; if (!WaitForResponseTimeout(CMD_HF_MIFARE_READBL, &resp, 1500)) continue; if (resp.status != PM3_SUCCESS) continue; uint8_t *data = resp.data.asBytes; key64 = bytes_to_num(data + 10, 6); if (key64) { PrintAndLogEx(SUCCESS, "data: %s", sprint_hex(data + 10, 6)); e_sector[i].foundKey[1] = true; e_sector[i].Key[1] = key64; } } } jumptoend: PrintAndLogEx(NORMAL, ""); PrintAndLogEx(SUCCESS, _GREEN_("found keys:")); //print them printKeyTable(SectorsCnt, e_sector); // transfer them to the emulator if (transferToEml) { // fast push mode conn.block_after_ACK = true; for (int i = 0; i < SectorsCnt; i++) { mfEmlGetMem(keyBlock, FirstBlockOfSector(i) + NumBlocksPerSector(i) - 1, 1); if (e_sector[i].foundKey[0]) num_to_bytes(e_sector[i].Key[0], 6, keyBlock); if (e_sector[i].foundKey[1]) num_to_bytes(e_sector[i].Key[1], 6, &keyBlock[10]); if (i == SectorsCnt - 1) { // Disable fast mode on last packet conn.block_after_ACK = false; } mfEmlSetMem(keyBlock, FirstBlockOfSector(i) + NumBlocksPerSector(i) - 1, 1); } PrintAndLogEx(SUCCESS, "keys transferred to emulator memory."); } // Create dump file if (createDumpFile) { char *fptr = GenerateFilename("hf-mf-", "-key.bin"); if (createMfcKeyDump(fptr, SectorsCnt, e_sector) != PM3_SUCCESS) { PrintAndLogEx(ERR, "Failed to save keys to file"); free(e_sector); free(fptr); return PM3_ESOFT; } free(fptr); } free(e_sector); return PM3_SUCCESS; } static int CmdHF14AMfNestedHard(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mf hardnested", "Nested attack for hardened MIFARE Classic cards.\n" "`--i` set type of SIMD instructions. Without this flag programs autodetect it.\n" " or \n" " hf mf hardnested -r --tk [known target key]\n" "Add the known target key to check if it is present in the remaining key space\n" " hf mf hardnested --blk 0 -a -k A0A1A2A3A4A5 --tblk 4 --ta --tk FFFFFFFFFFFF\n" , "hf mf hardnested --blk 0 -a -k FFFFFFFFFFFF --tblk 4 --ta\n" "hf mf hardnested --blk 0 -a -k FFFFFFFFFFFF --tblk 4 --ta -w\n" "hf mf hardnested --blk 0 -a -k FFFFFFFFFFFF --tblk 4 --ta -f nonces.bin -w -s\n" "hf mf hardnested -r\n" "hf mf hardnested -r --tk a0a1a2a3a4a5\n" "hf mf hardnested -t --tk a0a1a2a3a4a5\n" "hf mf hardnested --blk 0 -a -k a0a1a2a3a4a5 --tblk 4 --ta --tk FFFFFFFFFFFF" ); void *argtable[] = { arg_param_begin, arg_str0("k", "key", "", "Key, 12 hex bytes"), // 1 arg_int0(NULL, "blk", "", "Input block number"), // 2 arg_lit0("a", NULL, "Input key A (def)"), // 3 arg_lit0("b", NULL, "Input key B"), arg_int0(NULL, "tblk", "", "Target block number"), arg_lit0(NULL, "ta", "Target key A"), arg_lit0(NULL, "tb", "Target key B"), arg_str0(NULL, "tk", "", "Target key, 12 hex bytes"), // 8 arg_str0("u", "uid", "", "R/W `hf-mf--nonces.bin` instead of default name"), arg_str0("f", "file", "", "R/W instead of default name"), arg_lit0("r", "read", "Read `hf-mf--nonces.bin` if tag present, otherwise `nonces.bin`, and start attack"), arg_lit0("s", "slow", "Slower acquisition (required by some non standard cards)"), arg_lit0("t", "tests", "Run tests"), arg_lit0("w", "wr", "Acquire nonces and UID, and write them to file `hf-mf--nonces.bin`"), arg_lit0(NULL, "in", "None (use CPU regular instruction set)"), #if defined(COMPILER_HAS_SIMD) arg_lit0(NULL, "im", "MMX"), arg_lit0(NULL, "is", "SSE2"), arg_lit0(NULL, "ia", "AVX"), arg_lit0(NULL, "i2", "AVX2"), #endif #if defined(COMPILER_HAS_SIMD_AVX512) arg_lit0(NULL, "i5", "AVX512"), #endif arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); int keylen = 0; uint8_t key[6] = {0}; CLIGetHexWithReturn(ctx, 1, key, &keylen); uint8_t blockno = arg_get_u32_def(ctx, 2, 0); uint8_t keytype = MF_KEY_A; if (arg_get_lit(ctx, 3) && arg_get_lit(ctx, 4)) { CLIParserFree(ctx); PrintAndLogEx(WARNING, "Input key type must be A or B"); return PM3_EINVARG; } else if (arg_get_lit(ctx, 4)) { keytype = MF_KEY_B; } uint8_t trg_blockno = arg_get_u32_def(ctx, 5, 0); uint8_t trg_keytype = MF_KEY_A; if (arg_get_lit(ctx, 6) && arg_get_lit(ctx, 7)) { CLIParserFree(ctx); PrintAndLogEx(WARNING, "Input key type must be A or B"); return PM3_EINVARG; } else if (arg_get_lit(ctx, 7)) { trg_keytype = MF_KEY_B; } int trg_keylen = 0; uint8_t trg_key[6] = {0}; CLIGetHexWithReturn(ctx, 8, trg_key, &trg_keylen); int uidlen = 0; char uid[14] = {0}; CLIParamStrToBuf(arg_get_str(ctx, 9), (uint8_t *)uid, sizeof(uid), &uidlen); int fnlen = 0; char filename[FILE_PATH_SIZE] = {0}; CLIParamStrToBuf(arg_get_str(ctx, 10), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); bool nonce_file_read = arg_get_lit(ctx, 11); bool slow = arg_get_lit(ctx, 12); bool tests = arg_get_lit(ctx, 13); bool nonce_file_write = arg_get_lit(ctx, 14); bool in = arg_get_lit(ctx, 15); #if defined(COMPILER_HAS_SIMD) bool im = arg_get_lit(ctx, 16); bool is = arg_get_lit(ctx, 17); bool ia = arg_get_lit(ctx, 18); bool i2 = arg_get_lit(ctx, 19); #endif #if defined(COMPILER_HAS_SIMD_AVX512) bool i5 = arg_get_lit(ctx, 20); #endif CLIParserFree(ctx); // set SIM instructions SetSIMDInstr(SIMD_AUTO); #if defined(COMPILER_HAS_SIMD_AVX512) if (i5) SetSIMDInstr(SIMD_AVX512); #endif #if defined(COMPILER_HAS_SIMD) if (i2) SetSIMDInstr(SIMD_AVX2); if (ia) SetSIMDInstr(SIMD_AVX); if (is) SetSIMDInstr(SIMD_SSE2); if (im) SetSIMDInstr(SIMD_MMX); #endif if (in) SetSIMDInstr(SIMD_NONE); bool know_target_key = (trg_keylen); if (nonce_file_read) { char *fptr = GenerateFilename("hf-mf-", "-nonces.bin"); if (fptr == NULL) strncpy(filename, "nonces.bin", FILE_PATH_SIZE - 1); else strncpy(filename, fptr, FILE_PATH_SIZE - 1); free(fptr); } if (nonce_file_write) { char *fptr = GenerateFilename("hf-mf-", "-nonces.bin"); if (fptr == NULL) return 1; strncpy(filename, fptr, FILE_PATH_SIZE - 1); free(fptr); } if (uidlen) { snprintf(filename, FILE_PATH_SIZE, "hf-mf-%s-nonces.bin", uid); } if (know_target_key == false && nonce_file_read == false) { // check if tag doesn't have static nonce if (detect_classic_static_nonce() == NONCE_STATIC) { PrintAndLogEx(WARNING, "Static nonce detected. Quitting..."); PrintAndLogEx(HINT, "\tTry use `" _YELLOW_("hf mf staticnested") "`"); return PM3_EOPABORTED; } uint64_t key64 = 0; // check if we can authenticate to sector if (mfCheckKeys(blockno, keytype, true, 1, key, &key64) != PM3_SUCCESS) { PrintAndLogEx(WARNING, "Key is wrong. Can't authenticate to block: %3d key type: %c", blockno, (keytype == MF_KEY_B) ? 'B' : 'A'); return PM3_EWRONGANSWER; } } PrintAndLogEx(INFO, "Target block no " _YELLOW_("%3d") ", target key type: " _YELLOW_("%c") ", known target key: " _YELLOW_("%02x%02x%02x%02x%02x%02x%s"), trg_blockno, (trg_keytype == MF_KEY_B) ? 'B' : 'A', trg_key[0], trg_key[1], trg_key[2], trg_key[3], trg_key[4], trg_key[5], know_target_key ? "" : " (not set)" ); PrintAndLogEx(INFO, "File action: " _YELLOW_("%s") ", Slow: " _YELLOW_("%s") ", Tests: " _YELLOW_("%d"), nonce_file_write ? "write" : nonce_file_read ? "read" : "none", slow ? "Yes" : "No", tests); uint64_t foundkey = 0; int16_t isOK = mfnestedhard(blockno, keytype, key, trg_blockno, trg_keytype, know_target_key ? trg_key : NULL, nonce_file_read, nonce_file_write, slow, tests, &foundkey, filename); if ((tests == 0) && IfPm3Iso14443a()) { DropField(); } if (isOK) { switch (isOK) { case 1 : PrintAndLogEx(ERR, "Error: No response from Proxmark3.\n"); break; case 2 : PrintAndLogEx(NORMAL, "Button pressed. Aborted.\n"); break; default : break; } return 2; } return 0; } static int CmdHF14AMfAutoPWN(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mf autopwn", "This command automates the key recovery process on MIFARE Classic cards.\n" "It uses the fchk, chk, darkside, nested, hardnested and staticnested to recover keys.\n" "If all keys are found, it try dumping card content both to file and emulator memory.", "hf mf autopwn\n" "hf mf autopwn -s 0 -a -k FFFFFFFFFFFF --> target MFC 1K card, Sector 0 with known key A 'FFFFFFFFFFFF'\n" "hf mf autopwn --1k -f mfc_default_keys --> target MFC 1K card, default dictionary\n" "hf mf autopwn --1k -s 0 -a -k FFFFFFFFFFFF -f mfc_default_keys --> combo of the two above samples" ); void *argtable[] = { arg_param_begin, arg_str0("k", "key", "", "Known key, 12 hex bytes"), arg_int0("s", "sector", "", "Input sector number"), arg_lit0("a", NULL, "Input key A (def)"), arg_lit0("b", NULL, "Input key B"), arg_str0("f", "file", "", "filename of dictionary"), arg_lit0("s", "slow", "Slower acquisition (required by some non standard cards)"), arg_lit0("l", "legacy", "legacy mode (use the slow `hf mf chk`)"), arg_lit0("v", "verbose", "verbose output (statistics)"), arg_lit0(NULL, "mini", "MIFARE Classic Mini / S20"), arg_lit0(NULL, "1k", "MIFARE Classic 1k / S50 (default)"), arg_lit0(NULL, "2k", "MIFARE Classic/Plus 2k"), arg_lit0(NULL, "4k", "MIFARE Classic 4k / S70"), arg_lit0(NULL, "in", "None (use CPU regular instruction set)"), #if defined(COMPILER_HAS_SIMD) arg_lit0(NULL, "im", "MMX"), arg_lit0(NULL, "is", "SSE2"), arg_lit0(NULL, "ia", "AVX"), arg_lit0(NULL, "i2", "AVX2"), #endif #if defined(COMPILER_HAS_SIMD_AVX512) arg_lit0(NULL, "i5", "AVX512"), #endif arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); int keylen = 0; uint8_t key[6] = {0}; int32_t res = CLIParamHexToBuf(arg_get_str(ctx, 1), key, sizeof(key), &keylen); if (res) { CLIParserFree(ctx); PrintAndLogEx(FAILED, "Error parsing key bytes"); return PM3_EINVARG; } bool know_target_key = (keylen == 6); uint8_t sectorno = arg_get_u32_def(ctx, 2, 0); uint8_t keytype = MF_KEY_A; if (arg_get_lit(ctx, 3) && arg_get_lit(ctx, 4)) { CLIParserFree(ctx); PrintAndLogEx(WARNING, "Input key type must be A or B"); return PM3_EINVARG; } else if (arg_get_lit(ctx, 4)) { keytype = MF_KEY_B; } int fnlen = 0; char filename[FILE_PATH_SIZE] = {0}; CLIParamStrToBuf(arg_get_str(ctx, 5), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); bool has_filename = (fnlen > 0); bool slow = arg_get_lit(ctx, 6); bool legacy_mfchk = arg_get_lit(ctx, 7); bool verbose = arg_get_lit(ctx, 8); bool m0 = arg_get_lit(ctx, 9); bool m1 = arg_get_lit(ctx, 10); bool m2 = arg_get_lit(ctx, 11); bool m4 = arg_get_lit(ctx, 12); bool in = arg_get_lit(ctx, 13); #if defined(COMPILER_HAS_SIMD) bool im = arg_get_lit(ctx, 14); bool is = arg_get_lit(ctx, 15); bool ia = arg_get_lit(ctx, 16); bool i2 = arg_get_lit(ctx, 17); #endif #if defined(COMPILER_HAS_SIMD_AVX512) bool i5 = arg_get_lit(ctx, 18); #endif CLIParserFree(ctx); //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 sector_cnt = MIFARE_1K_MAXSECTOR; uint16_t block_cnt = MIFARE_1K_MAXBLOCK; if (m0) { sector_cnt = MIFARE_MINI_MAXSECTOR; block_cnt = MIFARE_MINI_MAXBLOCK; } else if (m1) { sector_cnt = MIFARE_1K_MAXSECTOR; block_cnt = MIFARE_1K_MAXBLOCK; } else if (m2) { sector_cnt = MIFARE_2K_MAXSECTOR; block_cnt = MIFARE_2K_MAXBLOCK; } else if (m4) { sector_cnt = MIFARE_4K_MAXSECTOR; block_cnt = MIFARE_4K_MAXBLOCK; } else { PrintAndLogEx(WARNING, "Please specify a MIFARE Type"); return PM3_EINVARG; } // set SIM instructions SetSIMDInstr(SIMD_AUTO); #if defined(COMPILER_HAS_SIMD_AVX512) if (i5) SetSIMDInstr(SIMD_AVX512); #endif #if defined(COMPILER_HAS_SIMD) if (i2) SetSIMDInstr(SIMD_AVX2); if (ia) SetSIMDInstr(SIMD_AVX); if (is) SetSIMDInstr(SIMD_SSE2); if (im) SetSIMDInstr(SIMD_MMX); #endif if (in) SetSIMDInstr(SIMD_NONE); // Nested and Hardnested parameter uint64_t key64 = 0; bool calibrate = true; // Attack key storage variables uint8_t *keyBlock = NULL; uint32_t key_cnt = 0; sector_t *e_sector; uint8_t tmp_key[6] = {0}; // Nested and Hardnested returned status uint64_t foundkey = 0; int isOK = 0; int current_sector_i = 0, current_key_type_i = 0; // Dumping and transfere to simulater memory uint8_t block[16] = {0x00}; int bytes; // Settings int prng_type = PM3_EUNDEF; int has_staticnonce; uint8_t num_found_keys = 0; // ------------------------------ // create/initialize key storage structure res = initSectorTable(&e_sector, sector_cnt); if (res != sector_cnt) { free(e_sector); return PM3_EMALLOC; } // read uid to generate a filename for the key file char *fptr = GenerateFilename("hf-mf-", "-key.bin"); // check if tag doesn't have static nonce has_staticnonce = detect_classic_static_nonce(); // card prng type (weak=1 / hard=0 / select/card comm error = negative value) if (has_staticnonce == NONCE_NORMAL) { prng_type = detect_classic_prng(); if (prng_type < 0) { PrintAndLogEx(FAILED, "\nNo tag detected or other tag communication error"); free(e_sector); free(fptr); return prng_type; } } // print parameters if (verbose) { PrintAndLogEx(INFO, "======================= " _YELLOW_("SETTINGS") " ======================="); PrintAndLogEx(INFO, " card sectors .. " _YELLOW_("%d"), sector_cnt); PrintAndLogEx(INFO, " key supplied .. " _YELLOW_("%s"), know_target_key ? "True" : "False"); PrintAndLogEx(INFO, " known sector .. " _YELLOW_("%d"), sectorno); PrintAndLogEx(INFO, " keytype ....... " _YELLOW_("%c"), (keytype == MF_KEY_B) ? 'B' : 'A'); PrintAndLogEx(INFO, " known key ..... " _YELLOW_("%s"), sprint_hex(key, sizeof(key))); if (has_staticnonce == NONCE_STATIC) PrintAndLogEx(INFO, " card PRNG ..... " _YELLOW_("STATIC")); else if (has_staticnonce == NONCE_NORMAL) PrintAndLogEx(INFO, " card PRNG ..... " _YELLOW_("%s"), prng_type ? "WEAK" : "HARD"); else PrintAndLogEx(INFO, " card PRNG ..... " _YELLOW_("Could not determine PRNG,") " " _RED_("read failed.")); PrintAndLogEx(INFO, " dictionary .... " _YELLOW_("%s"), strlen(filename) ? filename : "NONE"); PrintAndLogEx(INFO, " legacy mode ... " _YELLOW_("%s"), legacy_mfchk ? "True" : "False"); PrintAndLogEx(INFO, "========================================================================"); } // Start the timer uint64_t t1 = msclock(); // check the user supplied key if (know_target_key == false) { PrintAndLogEx(WARNING, "no known key was supplied, key recovery might fail"); } else { if (verbose) { PrintAndLogEx(INFO, "======================= " _YELLOW_("START KNOWN KEY ATTACK") " ======================="); } if (mfCheckKeys(FirstBlockOfSector(sectorno), keytype, true, 1, key, &key64) == PM3_SUCCESS) { PrintAndLogEx(INFO, "target sector %3u key type %c -- using valid key [ " _GREEN_("%s") "] (used for nested / hardnested attack)", sectorno, (keytype == MF_KEY_B) ? 'B' : 'A', sprint_hex(key, sizeof(key)) ); // Store the key for the nested / hardnested attack (if supplied by the user) e_sector[sectorno].Key[keytype] = key64; e_sector[sectorno].foundKey[keytype] = 'U'; ++num_found_keys; } else { know_target_key = false; PrintAndLogEx(FAILED, "Key is wrong. Can't authenticate to sector"_RED_("%3d") " key type "_RED_("%c") " key " _RED_("%s"), sectorno, (keytype == MF_KEY_B) ? 'B' : 'A', sprint_hex(key, sizeof(key)) ); PrintAndLogEx(WARNING, "falling back to dictionary"); } // Check if the user supplied key is used by other sectors for (int i = 0; i < sector_cnt; i++) { for (int j = MF_KEY_A; j <= MF_KEY_B; j++) { if (e_sector[i].foundKey[j] == 0) { if (mfCheckKeys(FirstBlockOfSector(i), j, true, 1, key, &key64) == PM3_SUCCESS) { e_sector[i].Key[j] = bytes_to_num(key, 6); e_sector[i].foundKey[j] = 'U'; // If the user supplied secctor / keytype was wrong --> just be nice and correct it ;) if (know_target_key == false) { num_to_bytes(e_sector[i].Key[j], 6, key); know_target_key = true; sectorno = i; keytype = j; PrintAndLogEx(SUCCESS, "target sector %3u key type %c -- found valid key [ " _GREEN_("%s") " ] (used for nested / hardnested attack)", i, (j == MF_KEY_B) ? 'B' : 'A', sprint_hex_inrow(key, sizeof(key)) ); } else { PrintAndLogEx(SUCCESS, "target sector %3u key type %c -- found valid key [ " _GREEN_("%s") " ]", i, (j == MF_KEY_B) ? 'B' : 'A', sprint_hex_inrow(key, sizeof(key)) ); } ++num_found_keys; } } } } if (num_found_keys == sector_cnt * 2) { goto all_found; } } bool load_success = true; // Load the dictionary if (has_filename) { res = loadFileDICTIONARY_safe(filename, (void **) &keyBlock, 6, &key_cnt); if (res != PM3_SUCCESS || key_cnt == 0 || keyBlock == NULL) { PrintAndLogEx(FAILED, "An error occurred while loading the dictionary! (we will use the default keys now)"); if (keyBlock != NULL) { free(keyBlock); } load_success = false; } } if (has_filename == false || load_success == false) { keyBlock = calloc(ARRAYLEN(g_mifare_default_keys), 6); if (keyBlock == NULL) { free(e_sector); free(fptr); return PM3_EMALLOC; } for (int cnt = 0; cnt < ARRAYLEN(g_mifare_default_keys); cnt++) { num_to_bytes(g_mifare_default_keys[cnt], 6, keyBlock + cnt * 6); } key_cnt = ARRAYLEN(g_mifare_default_keys); PrintAndLogEx(SUCCESS, "loaded " _GREEN_("%2d") " keys from hardcoded default array", key_cnt); } // Use the dictionary to find sector keys on the card if (verbose) PrintAndLogEx(INFO, "======================= " _YELLOW_("START DICTIONARY ATTACK") " ======================="); if (legacy_mfchk) { PrintAndLogEx(INFO, "." NOLF); // Check all the sectors for (int i = 0; i < sector_cnt; i++) { for (int j = 0; j < 2; j++) { // Check if the key is known if (e_sector[i].foundKey[j] == 0) { for (uint32_t k = 0; k < key_cnt; k++) { PrintAndLogEx(NORMAL, "." NOLF); fflush(stdout); if (mfCheckKeys(FirstBlockOfSector(i), j, true, 1, (keyBlock + (6 * k)), &key64) == PM3_SUCCESS) { e_sector[i].Key[j] = bytes_to_num((keyBlock + (6 * k)), 6); e_sector[i].foundKey[j] = 'D'; ++num_found_keys; break; } } } } } PrintAndLogEx(NORMAL, ""); } else { uint32_t chunksize = key_cnt > (PM3_CMD_DATA_SIZE / 6) ? (PM3_CMD_DATA_SIZE / 6) : key_cnt; bool firstChunk = true, lastChunk = false; for (uint8_t strategy = 1; strategy < 3; strategy++) { PrintAndLogEx(INFO, "running strategy %u", strategy); // main keychunk loop for (uint32_t i = 0; i < key_cnt; i += chunksize) { if (kbd_enter_pressed()) { PrintAndLogEx(WARNING, "\naborted via keyboard!\n"); i = key_cnt; strategy = 3; break; // Exit the loop } uint32_t size = ((key_cnt - i) > chunksize) ? chunksize : key_cnt - i; // last chunk? if (size == key_cnt - i) lastChunk = true; res = mfCheckKeys_fast(sector_cnt, firstChunk, lastChunk, strategy, size, keyBlock + (i * 6), e_sector, false); if (firstChunk) firstChunk = false; // all keys, aborted if (res == PM3_SUCCESS) { i = key_cnt; strategy = 3; break; // Exit the loop } } // end chunks of keys firstChunk = true; lastChunk = false; } // end strategy } // Analyse the dictionary attack for (int i = 0; i < sector_cnt; i++) { for (int j = MF_KEY_A; j <= MF_KEY_B; j++) { if (e_sector[i].foundKey[j] == 1) { e_sector[i].foundKey[j] = 'D'; num_to_bytes(e_sector[i].Key[j], 6, tmp_key); // Store valid credentials for the nested / hardnested attack if none exist if (know_target_key == false) { num_to_bytes(e_sector[i].Key[j], 6, key); know_target_key = true; sectorno = i; keytype = j; PrintAndLogEx(SUCCESS, "target sector %3u key type %c -- found valid key [ " _GREEN_("%s") " ] (used for nested / hardnested attack)", i, (j == MF_KEY_B) ? 'B' : 'A', sprint_hex_inrow(tmp_key, sizeof(tmp_key)) ); } else { PrintAndLogEx(SUCCESS, "target sector %3u key type %c -- found valid key [ " _GREEN_("%s") " ]", i, (j == MF_KEY_B) ? 'B' : 'A', sprint_hex_inrow(tmp_key, sizeof(tmp_key)) ); } } } } // Check if at least one sector key was found if (know_target_key == false) { // Check if the darkside attack can be used if (prng_type && has_staticnonce != NONCE_STATIC) { if (verbose) { PrintAndLogEx(INFO, "======================= " _YELLOW_("START DARKSIDE ATTACK") " ======================="); } isOK = mfDarkside(FirstBlockOfSector(sectorno), keytype + 0x60, &key64); switch (isOK) { case -1 : PrintAndLogEx(WARNING, "\nButton pressed. Aborted."); goto noValidKeyFound; case -2 : PrintAndLogEx(FAILED, "\nCard is not vulnerable to Darkside attack (doesn't send NACK on authentication requests)."); goto noValidKeyFound; case -3 : PrintAndLogEx(FAILED, "\nCard is not vulnerable to Darkside attack (its random number generator is not predictable)."); goto noValidKeyFound; case -4 : PrintAndLogEx(FAILED, "\nCard is not vulnerable to Darkside attack (its random number generator seems to be based on the wellknown"); PrintAndLogEx(FAILED, "generating polynomial with 16 effective bits only, but shows unexpected behaviour."); goto noValidKeyFound; case -5 : PrintAndLogEx(WARNING, "\naborted via keyboard."); goto noValidKeyFound; default : PrintAndLogEx(SUCCESS, "\nFound valid key [ " _GREEN_("%012" PRIx64) " ]\n", key64); break; } // Store the keys num_to_bytes(key64, 6, key); e_sector[sectorno].Key[keytype] = key64; e_sector[sectorno].foundKey[keytype] = 'S'; PrintAndLogEx(SUCCESS, "target sector %3u key type %c -- found valid key [ " _GREEN_("%012" PRIx64) " ] (used for nested / hardnested attack)", sectorno, (keytype == MF_KEY_B) ? 'B' : 'A', key64 ); } else { noValidKeyFound: PrintAndLogEx(FAILED, "No usable key was found!"); free(keyBlock); free(e_sector); free(fptr); return PM3_ESOFT; } } free(keyBlock); // Clear the needed variables num_to_bytes(0, 6, tmp_key); bool nested_failed = false; // Iterate over each sector and key(A/B) for (current_sector_i = 0; current_sector_i < sector_cnt; current_sector_i++) { for (current_key_type_i = 0; current_key_type_i < 2; current_key_type_i++) { // If the key is already known, just skip it if (e_sector[current_sector_i].foundKey[current_key_type_i] == 0) { // Try the found keys are reused if (bytes_to_num(tmp_key, 6) != 0) { // The fast check --> mfCheckKeys_fast(sector_cnt, true, true, 2, 1, tmp_key, e_sector, false); // Returns false keys, so we just stick to the slower mfchk. for (int i = 0; i < sector_cnt; i++) { for (int j = MF_KEY_A; j <= MF_KEY_B; j++) { // Check if the sector key is already broken if (e_sector[i].foundKey[j]) continue; // Check if the key works if (mfCheckKeys(FirstBlockOfSector(i), j, true, 1, tmp_key, &key64) == PM3_SUCCESS) { e_sector[i].Key[j] = bytes_to_num(tmp_key, 6); e_sector[i].foundKey[j] = 'R'; PrintAndLogEx(SUCCESS, "target sector %3u key type %c -- found valid key [ " _GREEN_("%s") " ]", i, (j == MF_KEY_B) ? 'B' : 'A', sprint_hex_inrow(tmp_key, sizeof(tmp_key)) ); } } } } // Clear the last found key num_to_bytes(0, 6, tmp_key); if (current_key_type_i == MF_KEY_B) { if (e_sector[current_sector_i].foundKey[0] && !e_sector[current_sector_i].foundKey[1]) { if (verbose) { PrintAndLogEx(INFO, "======================= " _YELLOW_("START READ B KEY ATTACK") " ======================="); PrintAndLogEx(INFO, "reading B key of sector %3d with key type %c", current_sector_i, (current_key_type_i == MF_KEY_B) ? 'B' : 'A'); } uint8_t sectrail = (FirstBlockOfSector(current_sector_i) + NumBlocksPerSector(current_sector_i) - 1); mf_readblock_t payload; payload.blockno = sectrail; payload.keytype = MF_KEY_A; num_to_bytes(e_sector[current_sector_i].Key[0], 6, payload.key); // KEY A 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; uint8_t *data = resp.data.asBytes; key64 = bytes_to_num(data + 10, 6); if (key64) { e_sector[current_sector_i].foundKey[current_key_type_i] = 'A'; e_sector[current_sector_i].Key[current_key_type_i] = key64; num_to_bytes(key64, 6, tmp_key); PrintAndLogEx(SUCCESS, "target sector %3u key type %c -- found valid key [ " _GREEN_("%s") " ]", current_sector_i, (current_key_type_i == MF_KEY_B) ? 'B' : 'A', sprint_hex_inrow(tmp_key, sizeof(tmp_key)) ); } else { if (verbose) { PrintAndLogEx(WARNING, "unknown B key: sector: %3d key type: %c", current_sector_i, (current_key_type_i == MF_KEY_B) ? 'B' : 'A' ); PrintAndLogEx(INFO, " -- reading the B key was not possible, maybe due to access rights?"); } } } } // Use the nested / hardnested attack skipReadBKey: if (e_sector[current_sector_i].foundKey[current_key_type_i] == 0) { if (has_staticnonce == NONCE_STATIC) goto tryStaticnested; if (prng_type && (nested_failed == false)) { uint8_t retries = 0; if (verbose) { PrintAndLogEx(INFO, "======================= " _YELLOW_("START NESTED ATTACK") " ======================="); PrintAndLogEx(INFO, "sector no %3d, target key type %c", current_sector_i, (current_key_type_i == MF_KEY_B) ? 'B' : 'A'); } tryNested: isOK = mfnested(FirstBlockOfSector(sectorno), keytype, key, FirstBlockOfSector(current_sector_i), current_key_type_i, tmp_key, calibrate); switch (isOK) { case PM3_ETIMEOUT: { PrintAndLogEx(ERR, "\nError: No response from Proxmark3."); free(e_sector); free(fptr); return PM3_ESOFT; } case PM3_EOPABORTED: { PrintAndLogEx(WARNING, "\nButton pressed. Aborted."); free(e_sector); free(fptr); return PM3_EOPABORTED; } case PM3_EFAILED: { PrintAndLogEx(FAILED, "Tag isn't vulnerable to Nested Attack (PRNG is probably not predictable)."); PrintAndLogEx(FAILED, "Nested attack failed --> try hardnested"); goto tryHardnested; } case PM3_ESOFT: { // key not found calibrate = false; // this can happen on some old cards, it's worth trying some more before switching to slower hardnested if (retries++ < MIFARE_SECTOR_RETRY) { PrintAndLogEx(FAILED, "Nested attack failed, trying again (%i/%i)", retries, MIFARE_SECTOR_RETRY); goto tryNested; } else { PrintAndLogEx(FAILED, "Nested attack failed, moving to hardnested"); nested_failed = true; goto tryHardnested; } break; } case PM3_SUCCESS: { calibrate = false; e_sector[current_sector_i].Key[current_key_type_i] = bytes_to_num(tmp_key, 6); e_sector[current_sector_i].foundKey[current_key_type_i] = 'N'; break; } default: { PrintAndLogEx(ERR, "unknown Error.\n"); free(e_sector); free(fptr); return PM3_ESOFT; } } } else { tryHardnested: // If the nested attack fails then we try the hardnested attack if (verbose) { PrintAndLogEx(INFO, "======================= " _YELLOW_("START HARDNESTED ATTACK") " ======================="); PrintAndLogEx(INFO, "sector no %3d, target key type %c, Slow %s", current_sector_i, (current_key_type_i == MF_KEY_B) ? 'B' : 'A', slow ? "Yes" : "No"); } isOK = mfnestedhard(FirstBlockOfSector(sectorno), keytype, key, FirstBlockOfSector(current_sector_i), current_key_type_i, NULL, false, false, slow, 0, &foundkey, NULL); DropField(); if (isOK) { switch (isOK) { case 1: { PrintAndLogEx(ERR, "\nError: No response from Proxmark3"); break; } case 2: { PrintAndLogEx(NORMAL, "\nButton pressed, user aborted"); break; } default: { break; } } free(e_sector); free(fptr); return PM3_ESOFT; } // Copy the found key to the tmp_key variale (for the following print statement, and the mfCheckKeys above) num_to_bytes(foundkey, 6, tmp_key); e_sector[current_sector_i].Key[current_key_type_i] = foundkey; e_sector[current_sector_i].foundKey[current_key_type_i] = 'H'; } if (has_staticnonce == NONCE_STATIC) { tryStaticnested: if (verbose) { PrintAndLogEx(INFO, "======================= " _YELLOW_("START STATIC NESTED ATTACK") " ======================="); PrintAndLogEx(INFO, "sector no %3d, target key type %c", current_sector_i, (current_key_type_i == MF_KEY_B) ? 'B' : 'A'); } isOK = mfStaticNested(sectorno, keytype, key, FirstBlockOfSector(current_sector_i), current_key_type_i, tmp_key); DropField(); switch (isOK) { case PM3_ETIMEOUT: { PrintAndLogEx(ERR, "\nError: No response from Proxmark3"); free(e_sector); free(fptr); return PM3_ESOFT; } case PM3_EOPABORTED: { PrintAndLogEx(WARNING, "\nButton pressed, user aborted"); free(e_sector); free(fptr); return PM3_EOPABORTED; } case PM3_SUCCESS: { e_sector[current_sector_i].Key[current_key_type_i] = bytes_to_num(tmp_key, 6); e_sector[current_sector_i].foundKey[current_key_type_i] = 'C'; break; } default: { break; } } } // Check if the key was found if (e_sector[current_sector_i].foundKey[current_key_type_i]) { PrintAndLogEx(SUCCESS, "target sector %3u key type %c -- found valid key [ " _GREEN_("%s") " ]", current_sector_i, (current_key_type_i == MF_KEY_B) ? 'B' : 'A', sprint_hex_inrow(tmp_key, sizeof(tmp_key)) ); } } } } } all_found: // Show the results to the user PrintAndLogEx(NORMAL, ""); PrintAndLogEx(SUCCESS, _GREEN_("found keys:")); printKeyTable(sector_cnt, e_sector); // Dump the keys PrintAndLogEx(NORMAL, ""); if (createMfcKeyDump(fptr, sector_cnt, e_sector) != PM3_SUCCESS) { PrintAndLogEx(ERR, "Failed to save keys to file"); } // clear emulator mem clearCommandBuffer(); SendCommandNG(CMD_HF_MIFARE_EML_MEMCLR, NULL, 0); PrintAndLogEx(SUCCESS, "transferring keys to simulator memory (Cmd Error: 04 can occur)"); for (current_sector_i = 0; current_sector_i < sector_cnt; current_sector_i++) { mfEmlGetMem(block, current_sector_i, 1); if (e_sector[current_sector_i].foundKey[0]) num_to_bytes(e_sector[current_sector_i].Key[0], 6, block); if (e_sector[current_sector_i].foundKey[1]) num_to_bytes(e_sector[current_sector_i].Key[1], 6, block + 10); mfEmlSetMem(block, FirstBlockOfSector(current_sector_i) + NumBlocksPerSector(current_sector_i) - 1, 1); } // use ecfill trick FastDumpWithEcFill(sector_cnt); bytes = block_cnt * MFBLOCK_SIZE; uint8_t *dump = calloc(bytes, sizeof(uint8_t)); if (dump == NULL) { PrintAndLogEx(ERR, "Fail, cannot allocate memory"); free(e_sector); free(fptr); return PM3_EMALLOC; } PrintAndLogEx(INFO, "downloading the card content from emulator memory"); if (!GetFromDevice(BIG_BUF_EML, dump, bytes, 0, NULL, 0, NULL, 2500, false)) { PrintAndLogEx(ERR, "Fail, transfer from device time-out"); free(e_sector); free(dump); free(fptr); return PM3_ETIMEOUT; } free(fptr); fptr = GenerateFilename("hf-mf-", "-dump"); if (fptr == NULL) { free(dump); free(e_sector); free(fptr); return PM3_ESOFT; } strcpy(filename, fptr); free(fptr); saveFile(filename, ".bin", dump, bytes); saveFileEML(filename, dump, bytes, MFBLOCK_SIZE); saveFileJSON(filename, jsfCardMemory, dump, bytes, NULL); // Generate and show statistics t1 = msclock() - t1; PrintAndLogEx(INFO, "autopwn execution time: " _YELLOW_("%.0f") " seconds", (float)t1 / 1000.0); free(dump); free(e_sector); return PM3_SUCCESS; } static int CmdHF14AMfChk_fast(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mf fchk", "This is a improved checkkeys method speedwise. It checks MIFARE Classic tags sector keys against a dictionary file with keys", "hf mf fchk --mini -k FFFFFFFFFFFF --> Key recovery against MIFARE Mini\n" "hf mf fchk --1k -k FFFFFFFFFFFF --> Key recovery against MIFARE Classic 1k\n" "hf mf fchk --2k -k FFFFFFFFFFFF --> Key recovery against MIFARE 2k\n" "hf mf fchk --4k -k FFFFFFFFFFFF --> Key recovery against MIFARE 4k\n" "hf mf fchk --1k -f mfc_default_keys.dic --> Target 1K using default dictionary file\n" "hf mf fchk --1k --emu --> Target 1K, write to emulator memory\n" "hf mf fchk --1k --dump --> Target 1K, write to file\n" "hf mf fchk --1k --mem --> Target 1K, use dictionary from flashmemory"); void *argtable[] = { arg_param_begin, arg_strx0("k", "key", "", "Key specified as 12 hex symbols"), arg_lit0(NULL, "mini", "MIFARE Classic Mini / S20"), arg_lit0(NULL, "1k", "MIFARE Classic 1k / S50 (default)"), arg_lit0(NULL, "2k", "MIFARE Classic/Plus 2k"), arg_lit0(NULL, "4k", "MIFARE Classic 4k / S70"), arg_lit0(NULL, "emu", "Fill simulator keys from found keys"), arg_lit0(NULL, "dump", "Dump found keys to binary file"), arg_lit0(NULL, "mem", "Use dictionary from flashmemory"), arg_str0("f", "file", "", "filename of dictionary"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); int keylen = 0; uint8_t key[255 * 6] = {0}; CLIGetHexWithReturn(ctx, 1, key, &keylen); bool m0 = arg_get_lit(ctx, 2); bool m1 = arg_get_lit(ctx, 3); bool m2 = arg_get_lit(ctx, 4); bool m4 = arg_get_lit(ctx, 5); bool transferToEml = arg_get_lit(ctx, 6); bool createDumpFile = arg_get_lit(ctx, 7); bool use_flashmemory = arg_get_lit(ctx, 8); int fnlen = 0; char filename[FILE_PATH_SIZE] = {0}; CLIParamStrToBuf(arg_get_str(ctx, 9), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); CLIParserFree(ctx); //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 sectorsCnt = 1; if (m0) { sectorsCnt = MIFARE_MINI_MAXSECTOR; } else if (m1) { sectorsCnt = MIFARE_1K_MAXSECTOR; } else if (m2) { sectorsCnt = MIFARE_2K_MAXSECTOR; } else if (m4) { sectorsCnt = MIFARE_4K_MAXSECTOR; } else { PrintAndLogEx(WARNING, "Please specify a MIFARE Type"); return PM3_EINVARG; } // Handle Keys int keycnt = 0; uint32_t keyitems = ARRAYLEN(g_mifare_default_keys); uint8_t *keyBlock, *p; // Allocate memory for keys to be tested keyBlock = calloc(ARRAYLEN(g_mifare_default_keys), 6); if (keyBlock == NULL) return PM3_EMALLOC; // Copy default keys to list for (int cnt = 0; cnt < ARRAYLEN(g_mifare_default_keys); cnt++) num_to_bytes(g_mifare_default_keys[cnt], 6, (uint8_t *)(keyBlock + cnt * 6)); // Handle user supplied key if (keylen >= 6) { int numKeys = keylen / 6; p = realloc(keyBlock, 6 * (keyitems + numKeys)); if (!p) { PrintAndLogEx(FAILED, "cannot allocate memory for Keys"); free(keyBlock); return PM3_EMALLOC; } keyBlock = p; memcpy(keyBlock + 6 * keycnt, key, 6 * numKeys); for (int i = 0; i < numKeys; i++) { PrintAndLogEx(NORMAL, "[%2d] key %s", keycnt, sprint_hex((keyBlock + 6 * keycnt), 6)); keycnt++; } } // Handle user supplied dictionary file FILE *f; char buf[13]; if (fnlen > 0) { char *dict_path; int res = searchFile(&dict_path, DICTIONARIES_SUBDIR, filename, ".dic", false); if (res != PM3_SUCCESS) { free(keyBlock); return PM3_EFILE; } f = fopen(dict_path, "r"); if (!f) { PrintAndLogEx(FAILED, "File: " _YELLOW_("%s") ": not found or locked.", dict_path); free(dict_path); free(keyBlock); return PM3_EFILE; } free(dict_path); // load keys from dictionary file while (fgets(buf, sizeof(buf), f)) { if (strlen(buf) < 12 || buf[11] == '\n') continue; while (fgetc(f) != '\n' && !feof(f)) ; //goto next line if (buf[0] == '#') continue; //The line start with # is comment, skip // codesmell, only checks first char? if (!isxdigit(buf[0])) { PrintAndLogEx(FAILED, "File content error. '" _YELLOW_("%s")"' must include 12 HEX symbols", buf); continue; } buf[12] = 0; if (keyitems - keycnt < 2) { p = realloc(keyBlock, 6 * (keyitems += 64)); if (!p) { PrintAndLogEx(FAILED, "Cannot allocate memory for defKeys"); free(keyBlock); fclose(f); return PM3_EMALLOC; } keyBlock = p; } memset(keyBlock + 6 * keycnt, 0, 6); num_to_bytes(strtoll(buf, NULL, 16), 6, keyBlock + 6 * keycnt); //PrintAndLogEx(NORMAL, "check key[%2d] %012" PRIx64, keycnt, bytes_to_num(keyBlock + 6*keycnt, 6)); keycnt++; memset(buf, 0, sizeof(buf)); } fclose(f); PrintAndLogEx(SUCCESS, "Loaded %2d keys from " _YELLOW_("%s"), keycnt, filename); } if (keycnt == 0 && !use_flashmemory) { PrintAndLogEx(SUCCESS, "No key specified, trying default keys"); for (; keycnt < ARRAYLEN(g_mifare_default_keys); keycnt++) PrintAndLogEx(NORMAL, "[%2d] %02x%02x%02x%02x%02x%02x", keycnt, (keyBlock + 6 * keycnt)[0], (keyBlock + 6 * keycnt)[1], (keyBlock + 6 * keycnt)[2], (keyBlock + 6 * keycnt)[3], (keyBlock + 6 * keycnt)[4], (keyBlock + 6 * keycnt)[5]); } // create/initialize key storage structure sector_t *e_sector = NULL; int32_t res = initSectorTable(&e_sector, sectorsCnt); if (res != sectorsCnt) { free(keyBlock); return PM3_EMALLOC; } uint32_t chunksize = keycnt > (PM3_CMD_DATA_SIZE / 6) ? (PM3_CMD_DATA_SIZE / 6) : keycnt; bool firstChunk = true, lastChunk = false; int i = 0; // time uint64_t t1 = msclock(); if (use_flashmemory) { PrintAndLogEx(SUCCESS, "Using dictionary in flash memory"); mfCheckKeys_fast(sectorsCnt, true, true, 1, 0, keyBlock, e_sector, use_flashmemory); } else { // strategys. 1= deep first on sector 0 AB, 2= width first on all sectors for (uint8_t strategy = 1; strategy < 3; strategy++) { PrintAndLogEx(INFO, "Running strategy %u", strategy); // main keychunk loop for (i = 0; i < keycnt; i += chunksize) { if (kbd_enter_pressed()) { PrintAndLogEx(WARNING, "\naborted via keyboard!\n"); goto out; } uint32_t size = ((keycnt - i) > chunksize) ? chunksize : keycnt - i; // last chunk? if (size == keycnt - i) lastChunk = true; res = mfCheckKeys_fast(sectorsCnt, firstChunk, lastChunk, strategy, size, keyBlock + (i * 6), e_sector, false); if (firstChunk) firstChunk = false; // all keys, aborted if (res == PM3_SUCCESS || res == 2) goto out; } // end chunks of keys firstChunk = true; lastChunk = false; } // end strategy } out: t1 = msclock() - t1; PrintAndLogEx(INFO, "time in checkkeys (fast) " _YELLOW_("%.1fs") "\n", (float)(t1 / 1000.0)); // check.. uint8_t found_keys = 0; for (i = 0; i < sectorsCnt; ++i) { if (e_sector[i].foundKey[0]) found_keys++; if (e_sector[i].foundKey[1]) found_keys++; } if (found_keys == 0) { PrintAndLogEx(WARNING, "No keys found"); } else { PrintAndLogEx(NORMAL, ""); PrintAndLogEx(SUCCESS, _GREEN_("found keys:")); printKeyTable(sectorsCnt, e_sector); if (use_flashmemory && found_keys == (sectorsCnt << 1)) { PrintAndLogEx(SUCCESS, "Card dumped as well. run " _YELLOW_("`%s %c`"), "hf mf esave", GetFormatFromSector(sectorsCnt) ); } if (transferToEml) { // fast push mode conn.block_after_ACK = true; uint8_t block[16] = {0x00}; for (i = 0; i < sectorsCnt; ++i) { uint8_t b = FirstBlockOfSector(i) + NumBlocksPerSector(i) - 1; mfEmlGetMem(block, b, 1); if (e_sector[i].foundKey[0]) num_to_bytes(e_sector[i].Key[0], 6, block); if (e_sector[i].foundKey[1]) num_to_bytes(e_sector[i].Key[1], 6, block + 10); if (i == sectorsCnt - 1) { // Disable fast mode on last packet conn.block_after_ACK = false; } mfEmlSetMem(block, b, 1); } PrintAndLogEx(SUCCESS, "Found keys have been transferred to the emulator memory"); if (found_keys == (sectorsCnt << 1)) { FastDumpWithEcFill(sectorsCnt); } } if (createDumpFile) { char *fptr = GenerateFilename("hf-mf-", "-key.bin"); if (createMfcKeyDump(fptr, sectorsCnt, e_sector) != PM3_SUCCESS) { PrintAndLogEx(ERR, "Failed to save keys to file"); } free(fptr); } } free(keyBlock); free(e_sector); PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; } static int CmdHF14AMfChk(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mf chk", "Check keys on MIFARE Classic card", "hf mf chk --mini -k FFFFFFFFFFFF --> Check all sectors, all keys against MIFARE Mini\n" "hf mf chk --1k -k FFFFFFFFFFFF --> Check all sectors, all keys against MIFARE Classic 1k\n" "hf mf chk --2k -k FFFFFFFFFFFF --> Check all sectors, all keys against MIFARE 2k\n" "hf mf chk --4k -k FFFFFFFFFFFF --> Check all sectors, all keys against MIFARE 4k\n" "hf mf chk --1k --emu --> Check all sectors, all keys, 1K, and write to emulator memory\n" "hf mf chk --1k --dump --> Check all sectors, all keys, 1K, and write to file\n" "hf mf chk -a --blk 0 -f mfc_default_keys.dic --> Check dictionary against block 0, key A"); void *argtable[] = { arg_param_begin, arg_strx0("k", "key", "", "Key specified as 12 hex symbols"), arg_int0(NULL, "blk", "", "Input block number"), arg_lit0("a", NULL, "Target Key A, if found also check Key B for duplicate"), arg_lit0("b", NULL, "Target Key B"), arg_lit0("*", "all", "Target both key A & B (default)"), arg_lit0(NULL, "mini", "MIFARE Classic Mini / S20"), arg_lit0(NULL, "1k", "MIFARE Classic 1k / S50 (default)"), arg_lit0(NULL, "2k", "MIFARE Classic/Plus 2k"), arg_lit0(NULL, "4k", "MIFARE Classic 4k / S70"), arg_lit0(NULL, "emu", "Fill simulator keys from found keys"), arg_lit0(NULL, "dump", "Dump found keys to binary file"), arg_str0("f", "file", "", "filename of dictionary"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); int keylen = 0; uint8_t key[255 * 6] = {0}; CLIGetHexWithReturn(ctx, 1, key, &keylen); int blockNo = arg_get_int_def(ctx, 2, -1); uint8_t keyType = 2; if ((arg_get_lit(ctx, 3) && arg_get_lit(ctx, 4)) || arg_get_lit(ctx, 5)) { keyType = 2; } else if (arg_get_lit(ctx, 3)) { keyType = MF_KEY_A; } else if (arg_get_lit(ctx, 4)) { keyType = MF_KEY_B; } bool m0 = arg_get_lit(ctx, 6); bool m1 = arg_get_lit(ctx, 7); bool m2 = arg_get_lit(ctx, 8); bool m4 = arg_get_lit(ctx, 9); bool transferToEml = arg_get_lit(ctx, 10); bool createDumpFile = arg_get_lit(ctx, 11); int fnlen = 0; char filename[FILE_PATH_SIZE] = {0}; CLIParamStrToBuf(arg_get_str(ctx, 12), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); CLIParserFree(ctx); //validations if ((m0 + m1 + m2 + m4) > 1) { PrintAndLogEx(WARNING, "Only specify one MIFARE Type"); return PM3_EINVARG; } uint8_t SectorsCnt = 1; if (m0) { SectorsCnt = MIFARE_MINI_MAXSECTOR; } else if (m1) { SectorsCnt = MIFARE_1K_MAXSECTOR; } else if (m2) { SectorsCnt = MIFARE_2K_MAXSECTOR; } else if (m4) { SectorsCnt = MIFARE_4K_MAXSECTOR; } if ((blockNo == -1) && (SectorsCnt == 1)) { // Default to 1K if block number not specified SectorsCnt = MIFARE_1K_MAXSECTOR; } if (blockNo > -1) { if (SectorsCnt == 0) { PrintAndLogEx(WARNING, "Invalid MIFARE Type"); return PM3_EINVARG; } } else { blockNo = 3; } // Handle Keys int keycnt = 0; uint32_t keyitems = ARRAYLEN(g_mifare_default_keys); uint8_t *keyBlock, *p; // Allocate memory for keys to be tested keyBlock = calloc(ARRAYLEN(g_mifare_default_keys), 6); if (keyBlock == NULL) return PM3_EMALLOC; // Copy default keys to list for (int cnt = 0; cnt < ARRAYLEN(g_mifare_default_keys); cnt++) num_to_bytes(g_mifare_default_keys[cnt], 6, (uint8_t *)(keyBlock + cnt * 6)); // Handle user supplied key if (keylen >= 6) { int numKeys = keylen / 6; p = realloc(keyBlock, 6 * (keyitems + numKeys)); if (!p) { PrintAndLogEx(FAILED, "cannot allocate memory for Keys"); free(keyBlock); return PM3_EMALLOC; } keyBlock = p; memcpy(keyBlock + 6 * keycnt, key, 6 * numKeys); for (int i = 0; i < numKeys; i++) { PrintAndLogEx(NORMAL, "[%2d] key %s", keycnt, sprint_hex((keyBlock + 6 * keycnt), 6)); keycnt++; } } // Handle user supplied dictionary file FILE *f; char buf[13]; if (fnlen > 0) { char *dict_path; int res = searchFile(&dict_path, DICTIONARIES_SUBDIR, filename, ".dic", false); if (res != PM3_SUCCESS) { free(keyBlock); return PM3_EFILE; } f = fopen(dict_path, "r"); if (!f) { PrintAndLogEx(FAILED, "File: " _YELLOW_("%s") ": not found or locked.", dict_path); free(dict_path); free(keyBlock); return PM3_EFILE; } free(dict_path); // load keys from dictionary file while (fgets(buf, sizeof(buf), f)) { if (strlen(buf) < 12 || buf[11] == '\n') continue; while (fgetc(f) != '\n' && !feof(f)) ; //goto next line if (buf[0] == '#') continue; //The line start with # is comment, skip // codesmell, only checks first char? if (!isxdigit(buf[0])) { PrintAndLogEx(FAILED, "File content error. '" _YELLOW_("%s")"' must include 12 HEX symbols", buf); continue; } buf[12] = 0; if (keyitems - keycnt < 2) { p = realloc(keyBlock, 6 * (keyitems += 64)); if (!p) { PrintAndLogEx(FAILED, "Cannot allocate memory for defKeys"); free(keyBlock); fclose(f); return PM3_EMALLOC; } keyBlock = p; } memset(keyBlock + 6 * keycnt, 0, 6); num_to_bytes(strtoll(buf, NULL, 16), 6, keyBlock + 6 * keycnt); //PrintAndLogEx(NORMAL, "check key[%2d] %012" PRIx64, keycnt, bytes_to_num(keyBlock + 6*keycnt, 6)); keycnt++; memset(buf, 0, sizeof(buf)); } fclose(f); PrintAndLogEx(SUCCESS, "Loaded %2d keys from " _YELLOW_("%s"), keycnt, filename); } uint64_t key64 = 0; if (keycnt == 0) { PrintAndLogEx(INFO, "No key specified, trying default keys"); for (; keycnt < ARRAYLEN(g_mifare_default_keys); keycnt++) PrintAndLogEx(NORMAL, "[%2d] %02x%02x%02x%02x%02x%02x", keycnt, (keyBlock + 6 * keycnt)[0], (keyBlock + 6 * keycnt)[1], (keyBlock + 6 * keycnt)[2], (keyBlock + 6 * keycnt)[3], (keyBlock + 6 * keycnt)[4], (keyBlock + 6 * keycnt)[5] ); } // create/initialize key storage structure sector_t *e_sector = NULL; int32_t res = initSectorTable(&e_sector, SectorsCnt); if (res != SectorsCnt) { free(keyBlock); return PM3_EMALLOC; } uint8_t trgKeyType = MF_KEY_A; uint16_t max_keys = keycnt > KEYS_IN_BLOCK ? KEYS_IN_BLOCK : keycnt; PrintAndLogEx(INFO, "Start check for keys..."); PrintAndLogEx(INFO, "." NOLF); // fast push mode conn.block_after_ACK = true; // clear trace log by first check keys call only bool clearLog = true; // time uint64_t t1 = msclock(); // check keys. for (trgKeyType = (keyType == 2) ? 0 : keyType; trgKeyType < 2; (keyType == 2) ? (++trgKeyType) : (trgKeyType = 2)) { // loop sectors but block is used as to keep track of from which blocks to test int b = blockNo; for (int i = 0; i < SectorsCnt; ++i) { // skip already found keys. if (e_sector[i].foundKey[trgKeyType]) continue; for (uint16_t c = 0; c < keycnt; c += max_keys) { PrintAndLogEx(NORMAL, "." NOLF); fflush(stdout); if (kbd_enter_pressed()) { PrintAndLogEx(WARNING, "\naborted via keyboard!\n"); goto out; } uint16_t size = keycnt - c > max_keys ? max_keys : keycnt - c; if (mfCheckKeys(b, trgKeyType, clearLog, size, &keyBlock[6 * c], &key64) == PM3_SUCCESS) { e_sector[i].Key[trgKeyType] = key64; e_sector[i].foundKey[trgKeyType] = true; clearLog = false; break; } clearLog = false; } b < 127 ? (b += 4) : (b += 16); } } t1 = msclock() - t1; PrintAndLogEx(INFO, "\ntime in checkkeys " _YELLOW_("%.0f") " seconds\n", (float)t1 / 1000.0); // 20160116 If Sector A is found, but not Sector B, try just reading it of the tag? if (keyType != MF_KEY_B) { PrintAndLogEx(INFO, "testing to read key B..."); // loop sectors but block is used as to keep track of from which blocks to test int b = blockNo; for (int i = 0; i < SectorsCnt; i++) { // KEY A but not KEY B if (e_sector[i].foundKey[0] && !e_sector[i].foundKey[1]) { uint8_t s = GetSectorFromBlockNo(b); uint8_t sectrail = (FirstBlockOfSector(s) + NumBlocksPerSector(s) - 1); PrintAndLogEx(INFO, "Sector %u, First block of sector %u, Num of block %u", s, FirstBlockOfSector(s), NumBlocksPerSector(s)); PrintAndLogEx(INFO, "Reading block %d", sectrail); mf_readblock_t payload; payload.blockno = sectrail; payload.keytype = MF_KEY_A; // Use key A num_to_bytes(e_sector[i].Key[0], 6, payload.key); clearCommandBuffer(); SendCommandNG(CMD_HF_MIFARE_READBL, (uint8_t *)&payload, sizeof(mf_readblock_t)); PacketResponseNG resp; if (!WaitForResponseTimeout(CMD_HF_MIFARE_READBL, &resp, 1500)) continue; if (resp.status != PM3_SUCCESS) continue; uint8_t *data = resp.data.asBytes; key64 = bytes_to_num(data + 10, 6); if (key64) { PrintAndLogEx(NORMAL, "Data:%s", sprint_hex(data + 10, 6)); e_sector[i].foundKey[1] = 1; e_sector[i].Key[1] = key64; } } b < 127 ? (b += 4) : (b += 16); } } out: PrintAndLogEx(NORMAL, ""); PrintAndLogEx(SUCCESS, _GREEN_("found keys:")); //print keys if (SectorsCnt == 1) printKeyTableEx(SectorsCnt, e_sector, GetSectorFromBlockNo(blockNo)); else printKeyTable(SectorsCnt, e_sector); if (transferToEml) { // fast push mode conn.block_after_ACK = true; uint8_t block[16] = {0x00}; for (int i = 0; i < SectorsCnt; ++i) { uint8_t blockno = FirstBlockOfSector(i) + NumBlocksPerSector(i) - 1; mfEmlGetMem(block, blockno, 1); if (e_sector[i].foundKey[0]) num_to_bytes(e_sector[i].Key[0], 6, block); if (e_sector[i].foundKey[1]) num_to_bytes(e_sector[i].Key[1], 6, block + 10); if (i == SectorsCnt - 1) { // Disable fast mode on last packet conn.block_after_ACK = false; } mfEmlSetMem(block, blockno, 1); } PrintAndLogEx(SUCCESS, "Found keys have been transferred to the emulator memory"); } if (createDumpFile) { char *fptr = GenerateFilename("hf-mf-", "-key.bin"); if (createMfcKeyDump(fptr, SectorsCnt, e_sector) != PM3_SUCCESS) { PrintAndLogEx(ERR, "Failed to save keys to file"); } free(fptr); } free(keyBlock); free(e_sector); // Disable fast mode and send a dummy command to make it effective conn.block_after_ACK = false; SendCommandNG(CMD_PING, NULL, 0); if (!WaitForResponseTimeout(CMD_PING, NULL, 1000)) { PrintAndLogEx(WARNING, "command execution time out"); return PM3_ETIMEOUT; } PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; } void showSectorTable(sector_t *k_sector, uint8_t k_sectorsCount) { if (k_sector != NULL) { printKeyTable(k_sectorsCount, k_sector); free(k_sector); } } void readerAttack(sector_t *k_sector, uint8_t k_sectorsCount, nonces_t data, bool setEmulatorMem, bool verbose) { uint64_t key = 0; bool success = false; if (k_sector == NULL) { int32_t res = initSectorTable(&k_sector, k_sectorsCount); if (res != k_sectorsCount) { free(k_sector); return; } } success = mfkey32_moebius(&data, &key); if (success) { uint8_t sector = data.sector; uint8_t keytype = data.keytype; PrintAndLogEx(INFO, "Reader is trying authenticate with: Key %s, sector %02d: [%012" PRIx64 "]" , (keytype == MF_KEY_B) ? "B" : "A" , sector , key ); k_sector[sector].Key[keytype] = key; k_sector[sector].foundKey[keytype] = true; //set emulator memory for keys if (setEmulatorMem) { uint8_t memBlock[16] = {0, 0, 0, 0, 0, 0, 0xff, 0x0F, 0x80, 0x69, 0, 0, 0, 0, 0, 0}; num_to_bytes(k_sector[sector].Key[0], 6, memBlock); num_to_bytes(k_sector[sector].Key[1], 6, memBlock + 10); //iceman, guessing this will not work so well for 4K tags. PrintAndLogEx(INFO, "Setting Emulator Memory Block %02d: [%s]" , (sector * 4) + 3 , sprint_hex(memBlock, sizeof(memBlock)) ); mfEmlSetMem(memBlock, (sector * 4) + 3, 1); } } free(k_sector); } static int CmdHF14AMfSim(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mf sim", "Simulate MIFARE Classic card", "hf mf sim --mini --> MIFARE Mini\n" "hf mf sim --1k --> MIFARE Classic 1k (default)\n" "hf mf sim --1k -u 0a0a0a0a --> MIFARE Classic 1k with 4b UID\n" "hf mf sim --1k -u 11223344556677 --> MIFARE Classic 1k with 7b UID\n" "hf mf sim --1k -u 11223344 -i --crack --> Perform reader attack in interactive mode\n" "hf mf sim --2k --> MIFARE 2k\n" "hf mf sim --4k --> MIFARE 4k"); void *argtable[] = { arg_param_begin, arg_str0("u", "uid", "", "UID 4,7 or 10bytes. If not specified, the UID 4b/7b from emulator memory will be used"), arg_lit0(NULL, "mini", "MIFARE Classic Mini / S20"), arg_lit0(NULL, "1k", "MIFARE Classic 1k / S50"), arg_lit0(NULL, "2k", "MIFARE Classic/Plus 2k"), arg_lit0(NULL, "4k", "MIFARE Classic 4k / S70"), arg_str0(NULL, "atqa", "", "Provide explicit ATQA (2 bytes, overrides option t)"), arg_str0(NULL, "sak", "", "Provide explicit SAK (1 bytes, overrides option t)"), arg_int0("n", "num", " ", "Automatically exit simulation after blocks have been read by reader. 0 = infinite"), arg_lit0("i", "interactive", "Console will not be returned until simulation finishes or is aborted"), arg_lit0("x", NULL, "Performs the 'reader attack', nr/ar attack against a reader"), arg_lit0("e", "emukeys", "Fill simulator keys from found keys"), arg_lit0("v", "verbose", "verbose output"), arg_lit0(NULL, "cve", "trigger CVE 2021_0430"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); uint16_t flags = 0; int uidlen = 0; uint8_t uid[10] = {0}; CLIGetHexWithReturn(ctx, 1, uid, &uidlen); char uidsize[8] = {0}; if (uidlen > 0) { switch (uidlen) { case 10: flags |= FLAG_10B_UID_IN_DATA; snprintf(uidsize, sizeof(uidsize), "10 byte"); break; case 7: flags |= FLAG_7B_UID_IN_DATA; snprintf(uidsize, sizeof(uidsize), "7 byte"); break; case 4: flags |= FLAG_4B_UID_IN_DATA; snprintf(uidsize, sizeof(uidsize), "4 byte"); break; default: PrintAndLogEx(WARNING, "Invalid parameter for UID"); CLIParserFree(ctx); return PM3_EINVARG; } } bool m0 = arg_get_lit(ctx, 2); bool m1 = arg_get_lit(ctx, 3); bool m2 = arg_get_lit(ctx, 4); bool m4 = arg_get_lit(ctx, 5); int atqalen = 0; uint8_t atqa[2] = {0}; CLIGetHexWithReturn(ctx, 6, atqa, &atqalen); int saklen = 0; uint8_t sak[1] = {0}; CLIGetHexWithReturn(ctx, 7, sak, &saklen); uint8_t exitAfterNReads = arg_get_u32_def(ctx, 8, 0); if (arg_get_lit(ctx, 9)) { flags |= FLAG_INTERACTIVE; } if (arg_get_lit(ctx, 10)) { flags |= FLAG_NR_AR_ATTACK; } bool setEmulatorMem = arg_get_lit(ctx, 11); bool verbose = arg_get_lit(ctx, 12); if (arg_get_lit(ctx, 13)) { flags |= FLAG_CVE21_0430; } CLIParserFree(ctx); nonces_t data[1]; sector_t *k_sector = NULL; //Validations if (atqalen > 0) { if (atqalen != 2) { PrintAndLogEx(WARNING, "Wrong ATQA length"); return PM3_EINVARG; } flags |= FLAG_FORCED_ATQA; } if (saklen > 0) { if (saklen != 1) { PrintAndLogEx(WARNING, "Wrong SAK length"); return PM3_EINVARG; } flags |= FLAG_FORCED_SAK; } // Use UID, SAK, ATQA from EMUL, if uid not defined if ((flags & (FLAG_4B_UID_IN_DATA | FLAG_7B_UID_IN_DATA | FLAG_10B_UID_IN_DATA)) == 0) { flags |= FLAG_UID_IN_EMUL; } uint8_t k_sectorsCount = 40; char csize[13] = { 0 }; if ((m0 + m1 + m2 + m4) > 1) { PrintAndLogEx(WARNING, "Only specify one MIFARE Type"); return PM3_EINVARG; } if (m0) { flags |= FLAG_MF_MINI; snprintf(csize, sizeof(csize), "MINI"); k_sectorsCount = MIFARE_MINI_MAXSECTOR; } else if (m1) { flags |= FLAG_MF_1K; snprintf(csize, sizeof(csize), "1K"); k_sectorsCount = MIFARE_1K_MAXSECTOR; } else if (m2) { flags |= FLAG_MF_2K; snprintf(csize, sizeof(csize), "2K with RATS"); k_sectorsCount = MIFARE_2K_MAXSECTOR; } else if (m4) { flags |= FLAG_MF_4K; snprintf(csize, sizeof(csize), "4K"); k_sectorsCount = MIFARE_4K_MAXSECTOR; } else { PrintAndLogEx(WARNING, "Please specify a MIFARE Type"); return PM3_EINVARG; } PrintAndLogEx(INFO, _YELLOW_("MIFARE %s") " | %s UID " _YELLOW_("%s") "" , csize , uidsize , (uidlen == 0) ? "N/A" : sprint_hex(uid, uidlen) ); PrintAndLogEx(INFO, "Options [ numreads: %d, flags: %d (0x%02x) ]" , exitAfterNReads , flags , flags); struct { uint16_t flags; uint8_t exitAfter; uint8_t uid[10]; uint16_t atqa; uint8_t sak; } PACKED payload; payload.flags = flags; payload.exitAfter = exitAfterNReads; memcpy(payload.uid, uid, uidlen); payload.atqa = (atqa[1] << 8) | atqa[0]; payload.sak = sak[0]; clearCommandBuffer(); SendCommandNG(CMD_HF_MIFARE_SIMULATE, (uint8_t *)&payload, sizeof(payload)); PacketResponseNG resp; if (flags & FLAG_INTERACTIVE) { PrintAndLogEx(INFO, "Press pm3-button or send another cmd to abort simulation"); while (!kbd_enter_pressed()) { if (!WaitForResponseTimeout(CMD_ACK, &resp, 1500)) continue; if (!(flags & FLAG_NR_AR_ATTACK)) break; if ((resp.oldarg[0] & 0xffff) != CMD_HF_MIFARE_SIMULATE) break; memcpy(data, resp.data.asBytes, sizeof(data)); readerAttack(k_sector, k_sectorsCount, data[0], setEmulatorMem, verbose); } showSectorTable(k_sector, k_sectorsCount); } return PM3_SUCCESS; } /* static int CmdHF14AMfKeyBrute(const char *Cmd) { uint8_t blockNo = 0, keytype = MF_KEY_A; uint8_t key[6] = {0, 0, 0, 0, 0, 0}; uint64_t foundkey = 0; char cmdp = tolower(param_getchar(Cmd, 0)); if (cmdp == 'h') return usage_hf14_keybrute(); // block number blockNo = param_get8(Cmd, 0); // keytype cmdp = tolower(param_getchar(Cmd, 1)); if (cmdp == 'b') keytype = MF_KEY_B; // key if (param_gethex(Cmd, 2, key, 12)) return usage_hf14_keybrute(); uint64_t t1 = msclock(); if (mfKeyBrute(blockNo, keytype, key, &foundkey)) PrintAndLogEx(SUCCESS, "found valid key: %012" PRIx64 " \n", foundkey); else PrintAndLogEx(FAILED, "key not found"); t1 = msclock() - t1; PrintAndLogEx(SUCCESS, "\ntime in keybrute " _YELLOW_("%.0f") " seconds\n", (float)t1 / 1000.0); return PM3_SUCCESS; } */ void printKeyTable(uint8_t sectorscnt, sector_t *e_sector) { return printKeyTableEx(sectorscnt, e_sector, 0); } void printKeyTableEx(uint8_t sectorscnt, sector_t *e_sector, uint8_t start_sector) { char strA[12 + 1] = {0}; char strB[12 + 1] = {0}; PrintAndLogEx(NORMAL, ""); PrintAndLogEx(SUCCESS, "|-----|----------------|---|----------------|---|"); PrintAndLogEx(SUCCESS, "| Sec | key A |res| key B |res|"); PrintAndLogEx(SUCCESS, "|-----|----------------|---|----------------|---|"); for (uint8_t i = 0; i < sectorscnt; i++) { snprintf(strA, sizeof(strA), "------------"); snprintf(strB, sizeof(strB), "------------"); if (e_sector[i].foundKey[0]) snprintf(strA, sizeof(strA), "%012" PRIx64, e_sector[i].Key[0]); if (e_sector[i].foundKey[1]) snprintf(strB, sizeof(strB), "%012" PRIx64, e_sector[i].Key[1]); if (e_sector[i].foundKey[0] > 1) { PrintAndLogEx(SUCCESS, "| "_YELLOW_("%03d")" | " _GREEN_("%s")" | " _YELLOW_("%c")" | " _GREEN_("%s")" | " _YELLOW_("%c")" |" , i , strA, e_sector[i].foundKey[0] , strB, e_sector[i].foundKey[1] ); } else { // keep track if we use start_sector or i... uint8_t s = start_sector; if (start_sector == 0) s = i; PrintAndLogEx(SUCCESS, "| "_YELLOW_("%03d")" | " _GREEN_("%s")" | " _YELLOW_("%d")" | " _GREEN_("%s")" | " _YELLOW_("%d")" |" , s , strA, e_sector[i].foundKey[0] , strB, e_sector[i].foundKey[1] ); } } PrintAndLogEx(SUCCESS, "|-----|----------------|---|----------------|---|"); if (e_sector[0].foundKey[0] > 1) { PrintAndLogEx(INFO, "( " _YELLOW_("D") ":Dictionary / " _YELLOW_("S") ":darkSide / " _YELLOW_("U") ":User / " _YELLOW_("R") ":Reused / " _YELLOW_("N") ":Nested / " _YELLOW_("H") ":Hardnested / " _YELLOW_("C") ":statiCnested / " _YELLOW_("A") ":keyA " " )" ); } else { PrintAndLogEx(SUCCESS, "( " _YELLOW_("0") ":Failed / " _YELLOW_("1") ":Success )"); } PrintAndLogEx(NORMAL, ""); } // EMULATOR COMMANDS static int CmdHF14AMfEGetBlk(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mf egetblk", "Get emulator memory block", "hf mf egetblk --blk 0 -> get block 0 (manufacturer)\n" "hf mf egetblk --blk 3 -v -> get block 3, decode sector trailer\n" ); void *argtable[] = { arg_param_begin, arg_int1("b", "blk", "", "block number"), arg_lit0("v", "verbose", "verbose output"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); int b = arg_get_int_def(ctx, 1, 0); bool verbose = arg_get_lit(ctx, 2); CLIParserFree(ctx); if (b > 255) { return PM3_EINVARG; } uint8_t blockno = (uint8_t)b; uint8_t data[16] = {0x00}; if (mfEmlGetMem(data, blockno, 1) == PM3_SUCCESS) { uint8_t sector = GetSectorFromBlockNo(blockno); mf_print_sector_hdr(sector); mf_print_block(blockno, data); } if (verbose) { decode_print_st(blockno, data); } else { PrintAndLogEx(NORMAL, ""); } return PM3_SUCCESS; } static int CmdHF14AMfEGetSc(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mf egetsc", "Get emulator memory sector", "hf mf egetsc -s 0" ); void *argtable[] = { arg_param_begin, arg_int1("s", "sec", "", "sector number"), arg_lit0("v", "verbose", "verbose output"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); int s = arg_get_int_def(ctx, 1, 0); bool verbose = arg_get_lit(ctx, 2); CLIParserFree(ctx); if (s > 39) { PrintAndLogEx(WARNING, "Sector number must be less then 40"); return PM3_EINVARG; } uint8_t sector = (uint8_t)s; mf_print_sector_hdr(sector); uint8_t blocks = NumBlocksPerSector(sector); uint8_t start = FirstBlockOfSector(sector); uint8_t data[16] = {0}; for (int i = 0; i < blocks; i++) { int res = mfEmlGetMem(data, start + i, 1); if (res == PM3_SUCCESS) { mf_print_block(start + i, data); } } if (verbose) { decode_print_st(start + blocks - 1, data); } else { PrintAndLogEx(NORMAL, ""); } return PM3_SUCCESS; } static int CmdHF14AMfEClear(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mf eclr", "It set card emulator memory to empty data blocks and key A/B FFFFFFFFFFFF", "hf mf eclr" ); void *argtable[] = { arg_param_begin, arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); CLIParserFree(ctx); clearCommandBuffer(); SendCommandNG(CMD_HF_MIFARE_EML_MEMCLR, NULL, 0); return PM3_SUCCESS; } static int CmdHF14AMfESet(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mf esetblk", "Set emulator memory block", "hf mf esetblk --blk 1 -d 000102030405060708090a0b0c0d0e0f" ); void *argtable[] = { arg_param_begin, arg_int1("b", "blk", "", "block number"), arg_str0("d", "data", "", "bytes to write, 16 hex bytes"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); int b = arg_get_int_def(ctx, 1, 0); uint8_t data[16] = {0x00}; int datalen = 0; int res = CLIParamHexToBuf(arg_get_str(ctx, 2), data, sizeof(data), &datalen); CLIParserFree(ctx); if (res) { PrintAndLogEx(FAILED, "Error parsing bytes"); return PM3_EINVARG; } if (b > 255) { return PM3_EINVARG; } if (datalen != sizeof(data)) { PrintAndLogEx(WARNING, "block data must include 16 HEX bytes. Got %i", datalen); return PM3_EINVARG; } // 1 - blocks count return mfEmlSetMem(data, b, 1); } int CmdHF14AMfELoad(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mf eload", "Load emulator memory with data from (bin/eml/json) dump file", "hf mf eload -f hf-mf-01020304.bin\n" "hf mf eload --4k -f hf-mf-01020304.eml\n" ); void *argtable[] = { arg_param_begin, arg_str1("f", "file", "", "filename of dump"), 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(NULL, "ul", "MIFARE Ultralight family"), arg_int0("q", "qty", "", "manually set number of blocks (overrides)"), 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 m0 = arg_get_lit(ctx, 2); bool m1 = arg_get_lit(ctx, 3); bool m2 = arg_get_lit(ctx, 4); bool m4 = arg_get_lit(ctx, 5); bool mu = arg_get_lit(ctx, 6); int numblks = arg_get_int_def(ctx, 7, -1); CLIParserFree(ctx); // validations if ((m0 + m1 + m2 + m4 + mu) > 1) { PrintAndLogEx(WARNING, "Only specify one MIFARE Type"); return PM3_EINVARG; } else if ((m0 + m1 + m2 + m4 + mu) == 0) { m1 = true; } uint8_t block_width = 16; uint16_t block_cnt = MIFARE_1K_MAXBLOCK; if (m0) { block_cnt = MIFARE_MINI_MAXBLOCK; } else if (m1) { block_cnt = MIFARE_1K_MAXBLOCK; } else if (m2) { block_cnt = MIFARE_2K_MAXBLOCK; } else if (m4) { block_cnt = MIFARE_4K_MAXBLOCK; } else if (mu) { block_cnt = 255; block_width = 4; } else { PrintAndLogEx(WARNING, "Please specify a MIFARE Type"); return PM3_EINVARG; } PrintAndLogEx(INFO, "%d blocks ( %u bytes ) to upload", block_cnt, block_cnt * block_width); if (numblks > 0) { block_cnt = MIN(numblks, block_cnt); PrintAndLogEx(INFO, "overriding number of blocks, will use %d blocks ( %u bytes )", block_cnt, block_cnt * block_width); } uint8_t *data = NULL; size_t datalen = 0; int res = PM3_SUCCESS; DumpFileType_t dftype = getfiletype(filename); switch (dftype) { case BIN: { res = loadFile_safe(filename, ".bin", (void **)&data, &datalen); break; } case EML: { res = loadFileEML_safe(filename, (void **)&data, &datalen); break; } case JSON: { data = calloc(MFBLOCK_SIZE * MIFARE_4K_MAXBLOCK, sizeof(uint8_t)); if (data == NULL) { PrintAndLogEx(WARNING, "Fail, cannot allocate memory"); return PM3_EMALLOC; } res = loadFileJSON(filename, (void *)data, MIFARE_4K_MAXBLOCK * MFBLOCK_SIZE, &datalen, NULL); break; } case DICTIONARY: { PrintAndLogEx(ERR, "Error: Only BIN/JSON/EML formats allowed"); free(data); return PM3_EINVARG; } } if (res != PM3_SUCCESS) { free(data); return PM3_EFILE; } // 64 or 256 blocks. if ((datalen % block_width) != 0) { PrintAndLogEx(FAILED, "File content error. Size doesn't match blockwidth "); free(data); return PM3_ESOFT; } // convert plain or old mfu format to new format if (block_width == 4) { res = convert_mfu_dump_format(&data, &datalen, true); if (res != PM3_SUCCESS) { PrintAndLogEx(FAILED, "Failed convert on load to new Ultralight/NTAG format"); free(data); return res; } mfu_dump_t *mfu_dump = (mfu_dump_t *)data; printMFUdumpEx(mfu_dump, mfu_dump->pages + 1, 0); // update expected blocks to match converted data. block_cnt = datalen / 4; PrintAndLogEx(INFO, "MIFARE Ultralight override, will use %d blocks ( %u bytes )", block_cnt, block_cnt * block_width); } PrintAndLogEx(INFO, "Uploading to emulator memory"); PrintAndLogEx(INFO, "." NOLF); // fast push mode conn.block_after_ACK = true; size_t offset = 0; int cnt = 0; while (datalen && cnt < block_cnt) { if (datalen == block_width) { // Disable fast mode on last packet conn.block_after_ACK = false; } if (mfEmlSetMem_xt(data + offset, cnt, 1, block_width) != PM3_SUCCESS) { PrintAndLogEx(FAILED, "Can't set emulator mem at block: %3d", cnt); free(data); return PM3_ESOFT; } PrintAndLogEx(NORMAL, "." NOLF); fflush(stdout); cnt++; offset += block_width; datalen -= block_width; } free(data); PrintAndLogEx(NORMAL, ""); if (block_width == 4) { PrintAndLogEx(HINT, "You are ready to simulate. See " _YELLOW_("`hf mfu sim -h`")); // MFU / NTAG if ((cnt != block_cnt)) { PrintAndLogEx(WARNING, "Warning, Ultralight/Ntag file content, Loaded %d blocks of expected %d blocks into emulator memory", cnt, block_cnt); return PM3_SUCCESS; } } else { PrintAndLogEx(HINT, "You are ready to simulate. See " _YELLOW_("`hf mf sim -h`")); // MFC if ((cnt != block_cnt)) { PrintAndLogEx(WARNING, "Error, file content, Only loaded %d blocks, must be %d blocks into emulator memory", cnt, block_cnt); return PM3_SUCCESS; } } PrintAndLogEx(INFO, "Done!"); return PM3_SUCCESS; } static int CmdHF14AMfESave(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mf esave", "Save emulator memory into three files (BIN/EML/JSON) ", "hf mf esave\n" "hf mf esave --4k\n" "hf mf esave --4k -f hf-mf-01020304.eml" ); void *argtable[] = { arg_param_begin, arg_str0("f", "file", "", "filename of dump"), 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 fnlen = 0; char filename[FILE_PATH_SIZE]; CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); bool m0 = arg_get_lit(ctx, 2); bool m1 = arg_get_lit(ctx, 3); bool m2 = arg_get_lit(ctx, 4); bool m4 = arg_get_lit(ctx, 5); CLIParserFree(ctx); // 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; } uint16_t block_cnt = MIFARE_1K_MAXBLOCK; if (m0) { block_cnt = MIFARE_MINI_MAXBLOCK; } else if (m1) { block_cnt = MIFARE_1K_MAXBLOCK; } else if (m2) { block_cnt = MIFARE_2K_MAXBLOCK; } else if (m4) { block_cnt = MIFARE_4K_MAXBLOCK; } int bytes = block_cnt * MFBLOCK_SIZE; // reserv memory uint8_t *dump = calloc(bytes, sizeof(uint8_t)); if (dump == NULL) { PrintAndLogEx(WARNING, "Fail, cannot allocate memory"); return PM3_EMALLOC; } memset(dump, 0, bytes); PrintAndLogEx(INFO, "downloading %u bytes from emulator memory", bytes); if (!GetFromDevice(BIG_BUF_EML, dump, bytes, 0, NULL, 0, NULL, 2500, false)) { PrintAndLogEx(WARNING, "Fail, transfer from device time-out"); free(dump); return PM3_ETIMEOUT; } // user supplied filename? if (fnlen < 1) { char *fptr = filename; fptr += snprintf(fptr, sizeof(filename), "hf-mf-"); FillFileNameByUID(fptr, dump, "-dump", 4); } saveFile(filename, ".bin", dump, bytes); saveFileEML(filename, dump, bytes, MFBLOCK_SIZE); saveFileJSON(filename, jsfCardMemory, dump, bytes, NULL); free(dump); return PM3_SUCCESS; } static int CmdHF14AMfEView(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mf eview", "It displays emulator memory", "hf mf eview\n" "hf mf eview --4k" ); void *argtable[] = { arg_param_begin, 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); bool m0 = arg_get_lit(ctx, 1); bool m1 = arg_get_lit(ctx, 2); bool m2 = arg_get_lit(ctx, 3); bool m4 = arg_get_lit(ctx, 4); CLIParserFree(ctx); // 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; } uint16_t block_cnt = MIFARE_1K_MAXBLOCK; if (m0) { block_cnt = MIFARE_MINI_MAXBLOCK; } else if (m1) { block_cnt = MIFARE_1K_MAXBLOCK; } else if (m2) { block_cnt = MIFARE_2K_MAXBLOCK; } else if (m4) { block_cnt = MIFARE_4K_MAXBLOCK; } else { PrintAndLogEx(WARNING, "Please specify a MIFARE Type"); return PM3_EINVARG; } int bytes = block_cnt * MFBLOCK_SIZE; uint8_t *dump = calloc(bytes, sizeof(uint8_t)); if (dump == NULL) { PrintAndLogEx(WARNING, "Fail, cannot allocate memory"); return PM3_EMALLOC; } memset(dump, 0, bytes); PrintAndLogEx(INFO, "downloading emulator memory"); if (!GetFromDevice(BIG_BUF_EML, dump, bytes, 0, NULL, 0, NULL, 2500, false)) { PrintAndLogEx(WARNING, "Fail, transfer from device time-out"); free(dump); return PM3_ETIMEOUT; } mf_print_blocks(block_cnt, dump); free(dump); return PM3_SUCCESS; } static int CmdHF14AMfECFill(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mf ecfill", "Dump card and transfer the data to emulator memory.\n" "Keys must be laid in the emulator memory", "hf mf ecfill --> use key type A\n" "hf mf ecfill --4k -b --> target 4K card with key type B" ); void *argtable[] = { arg_param_begin, arg_lit0("a", NULL, "input key type is key A(def)"), arg_lit0("b", NULL, "input key type is key B"), 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); uint8_t keytype = MF_KEY_A; if (arg_get_lit(ctx, 1) && arg_get_lit(ctx, 2)) { CLIParserFree(ctx); PrintAndLogEx(WARNING, "Input key type must be A or B"); return PM3_EINVARG; } else if (arg_get_lit(ctx, 2)) { keytype = MF_KEY_B; } 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); // 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 sectors_cnt = MIFARE_1K_MAXSECTOR; if (m0) { sectors_cnt = MIFARE_MINI_MAXSECTOR; } else if (m1) { sectors_cnt = MIFARE_1K_MAXSECTOR; } else if (m2) { sectors_cnt = MIFARE_2K_MAXSECTOR; } else if (m4) { sectors_cnt = MIFARE_4K_MAXSECTOR; } else { PrintAndLogEx(WARNING, "Please specify a MIFARE Type"); return PM3_EINVARG; } mfc_eload_t payload = { .sectorcnt = sectors_cnt, .keytype = keytype }; clearCommandBuffer(); SendCommandNG(CMD_HF_MIFARE_EML_LOAD, (uint8_t *)&payload, sizeof(payload)); // 2021, iceman: should get a response from device when its done. return PM3_SUCCESS; } static int CmdHF14AMfEKeyPrn(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mf ekeyprn", "Download and print the keys from emulator memory", "hf mf ekeyprn --1k --> print MFC 1K keyset\n" "hf mf ekeyprn -w --> write keys to binary file" ); void *argtable[] = { arg_param_begin, arg_lit0("w", "write", "write keys to binary file `hf-mf--key.bin`"), 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); bool create_dumpfile = arg_get_lit(ctx, 1); bool m0 = arg_get_lit(ctx, 2); bool m1 = arg_get_lit(ctx, 3); bool m2 = arg_get_lit(ctx, 4); bool m4 = arg_get_lit(ctx, 5); CLIParserFree(ctx); // 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 sectors_cnt = MIFARE_1K_MAXSECTOR; if (m0) { sectors_cnt = MIFARE_MINI_MAXSECTOR; } else if (m1) { sectors_cnt = MIFARE_1K_MAXSECTOR; } else if (m2) { sectors_cnt = MIFARE_2K_MAXSECTOR; } else if (m4) { sectors_cnt = MIFARE_4K_MAXSECTOR; } else { PrintAndLogEx(WARNING, "Please specify a MIFARE Type"); return PM3_EINVARG; } sector_t *e_sector = NULL; // create/initialize key storage structure int32_t res = initSectorTable(&e_sector, sectors_cnt); if (res != sectors_cnt) { free(e_sector); return PM3_EMALLOC; } // read UID from EMUL uint8_t data[16]; if (mfEmlGetMem(data, 0, 1) != PM3_SUCCESS) { PrintAndLogEx(WARNING, "error get block 0"); free(e_sector); return PM3_ESOFT; } // assuming 4byte UID. uint8_t uid[4]; memcpy(uid, data, sizeof(uid)); // download keys from EMUL for (int i = 0; i < sectors_cnt; i++) { if (mfEmlGetMem(data, FirstBlockOfSector(i) + NumBlocksPerSector(i) - 1, 1) != PM3_SUCCESS) { PrintAndLogEx(WARNING, "error get block %d", FirstBlockOfSector(i) + NumBlocksPerSector(i) - 1); e_sector[i].foundKey[0] = false; e_sector[i].foundKey[1] = false; } else { e_sector[i].foundKey[0] = true; e_sector[i].Key[0] = bytes_to_num(data, 6); e_sector[i].foundKey[1] = true; e_sector[i].Key[1] = bytes_to_num(data + 10, 6); } } // print keys printKeyTable(sectors_cnt, e_sector); // dump the keys if (create_dumpfile) { char filename[FILE_PATH_SIZE] = {0}; char *fptr = filename; fptr += snprintf(fptr, sizeof(filename), "hf-mf-"); FillFileNameByUID(fptr + strlen(fptr), uid, "-key", sizeof(uid)); createMfcKeyDump(filename, sectors_cnt, e_sector); } free(e_sector); return PM3_SUCCESS; } // CHINESE MAGIC COMMANDS static int CmdHF14AMfCSetUID(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mf csetuid", "Set UID, ATQA, and SAK for magic gen1a card", "hf mf csetuid -u 01020304\n" "hf mf csetuid -w -u 01020304 --atqa 0004 --sak 08" ); void *argtable[] = { arg_param_begin, arg_lit0("w", "wipe", "wipes card with backdoor cmd`"), arg_str0("u", "uid", "", "UID, 4/7 hex bytes"), arg_str0("a", "atqa", "", "ATQA, 2 hex bytes"), arg_str0("s", "sak", "", "SAK, 1 hex byte"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); uint8_t wipe_card = arg_get_lit(ctx, 1); int uidlen = 0; uint8_t uid[7] = {0x00}; CLIGetHexWithReturn(ctx, 2, uid, &uidlen); int alen = 0; uint8_t atqa[2] = {0x00}; CLIGetHexWithReturn(ctx, 3, atqa, &alen); int slen = 0; uint8_t sak[1] = {0x00}; CLIGetHexWithReturn(ctx, 4, sak, &slen); CLIParserFree(ctx); // sanity checks if (uidlen != 4 && uidlen != 7) { PrintAndLogEx(FAILED, "UID must be 4 or 7 hex bytes. Got %d", uidlen); return PM3_EINVARG; } if (alen && alen != 2) { PrintAndLogEx(FAILED, "ATQA must be 2 hex bytes. Got %d", alen); return PM3_EINVARG; } if (slen && slen != 1) { PrintAndLogEx(FAILED, "SAK must be 1 hex byte. Got %d", slen); return PM3_EINVARG; } uint8_t old_uid[7] = {0}; uint8_t verify_uid[7] = {0}; int res = mfCSetUID( uid, uidlen, (alen) ? atqa : NULL, (slen) ? sak : NULL, old_uid, verify_uid, wipe_card ); if (res) { PrintAndLogEx(ERR, "Can't set UID. error %d", res); return PM3_ESOFT; } res = memcmp(uid, verify_uid, uidlen); PrintAndLogEx(SUCCESS, "Old UID... %s", sprint_hex(old_uid, uidlen)); PrintAndLogEx(SUCCESS, "New UID... %s ( %s )", sprint_hex(verify_uid, uidlen), (res == 0) ? _GREEN_("verified") : _RED_("fail") ); return PM3_SUCCESS; } static int CmdHF14AMfCWipe(const char *cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mf cwipe", "Wipe gen1 magic chinese card.\n" "Set UID / ATQA / SAK / Data / Keys / Access to default values", "hf mf cwipe\n" "hf mf cwipe -u 09080706 -a 0004 -s 18 --> set UID, ATQA and SAK and wipe card"); void *argtable[] = { arg_param_begin, arg_str0("u", "uid", "", "UID, 4 hex bytes"), arg_str0("a", "atqa", "", "ATQA, 2 hex bytes"), arg_str0("s", "sak", "", "SAK, 1 hex byte"), arg_param_end }; CLIExecWithReturn(ctx, cmd, argtable, true); int uidlen = 0; uint8_t uid[8] = {0x00}; CLIGetHexWithReturn(ctx, 1, uid, &uidlen); int alen = 0; uint8_t atqa[2] = {0x00}; CLIGetHexWithReturn(ctx, 2, atqa, &alen); int slen = 0; uint8_t sak[1] = {0x00}; CLIGetHexWithReturn(ctx, 3, sak, &slen); CLIParserFree(ctx); if (uidlen && uidlen != 4) { PrintAndLogEx(ERR, "UID length must be 4 bytes, got %d", uidlen); return PM3_EINVARG; } if (alen && alen != 2) { PrintAndLogEx(ERR, "ATQA length must be 2 bytes, got %d", alen); return PM3_EINVARG; } if (slen && slen != 1) { PrintAndLogEx(ERR, "SAK length must be 1 byte, got %d", slen); return PM3_EINVARG; } int res = mfCWipe((uidlen) ? uid : NULL, (alen) ? atqa : NULL, (slen) ? sak : NULL); if (res) { PrintAndLogEx(ERR, "Can't wipe card. error %d", res); return PM3_ESOFT; } PrintAndLogEx(SUCCESS, "Card wiped successfully"); return PM3_SUCCESS; } static int CmdHF14AMfCSetBlk(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mf csetblk", "Set block data on a magic gen1a card", "hf mf csetblk --blk 1 -d 000102030405060708090a0b0c0d0e0f" ); void *argtable[] = { arg_param_begin, arg_int1("b", "blk", "", "block number"), arg_str0("d", "data", "", "bytes to write, 16 hex bytes"), arg_lit0("w", "wipe", "wipes card with backdoor cmd before writing"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); int b = arg_get_int_def(ctx, 1, -1); uint8_t data[MFBLOCK_SIZE] = {0x00}; int datalen = 0; CLIGetHexWithReturn(ctx, 2, data, &datalen); uint8_t wipe_card = arg_get_lit(ctx, 3); CLIParserFree(ctx); if (b < 0 || b >= MIFARE_1K_MAXBLOCK) { PrintAndLogEx(FAILED, "target block number out-of-range, got %i", b); return PM3_EINVARG; } if (datalen != MFBLOCK_SIZE) { PrintAndLogEx(FAILED, "expected 16 bytes data, got %i", datalen); return PM3_EINVARG; } uint8_t params = MAGIC_SINGLE; if (wipe_card) { params |= MAGIC_WIPE; } PrintAndLogEx(INFO, "Writing block number:%2d data:%s", b, sprint_hex_inrow(data, sizeof(data))); int res = mfCSetBlock(b, data, NULL, params); if (res) { PrintAndLogEx(ERR, "Can't write block. error=%d", res); return PM3_ESOFT; } return PM3_SUCCESS; } static int CmdHF14AMfCLoad(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mf cload", "Load magic gen1a card with data from (bin/eml/json) dump file\n" "or from emulator memory.", "hf mf cload --emu\n" "hf mf cload -f hf-mf-01020304.eml\n" ); void *argtable[] = { arg_param_begin, arg_str1("f", "file", "", "filename of dump"), arg_lit0(NULL, "emu", "from emulator memory"), 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 fill_from_emulator = arg_get_lit(ctx, 2); CLIParserFree(ctx); if (fill_from_emulator) { PrintAndLogEx(INFO, "Start upload to emulator memory"); PrintAndLogEx(INFO, "." NOLF); for (int b = 0; b < MIFARE_1K_MAXBLOCK; b++) { int flags = 0; uint8_t buf8[MFBLOCK_SIZE] = {0x00}; // read from emul memory if (mfEmlGetMem(buf8, b, 1)) { PrintAndLogEx(WARNING, "Can't read from emul block: %d", b); return PM3_ESOFT; } // switch on field and send magic sequence if (b == 0) { flags = MAGIC_INIT + MAGIC_WUPC; } // just write if (b == 1) { flags = 0; } // Done. Magic Halt and switch off field. if (b == ((MFBLOCK_SIZE * 4) - 1)) { flags = MAGIC_HALT + MAGIC_OFF; } // write to card if (mfCSetBlock(b, buf8, NULL, flags)) { PrintAndLogEx(WARNING, "Can't set magic card block: %d", b); return PM3_ESOFT; } PrintAndLogEx(NORMAL, "." NOLF); fflush(stdout); } PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; } uint8_t *data = NULL; size_t bytes_read = 0; int res = 0; DumpFileType_t dftype = getfiletype(filename); switch (dftype) { case BIN: { res = loadFile_safe(filename, ".bin", (void **)&data, &bytes_read); break; } case EML: { res = loadFileEML_safe(filename, (void **)&data, &bytes_read); break; } case JSON: { data = calloc(MFBLOCK_SIZE * MIFARE_4K_MAXBLOCK, sizeof(uint8_t)); if (data == NULL) { PrintAndLogEx(WARNING, "Fail, cannot allocate memory"); return PM3_EMALLOC; } res = loadFileJSON(filename, (void *)data, MIFARE_4K_MAXBLOCK * MFBLOCK_SIZE, &bytes_read, NULL); break; } case DICTIONARY: { PrintAndLogEx(ERR, "Error: Only BIN/JSON/EML formats allowed"); free(data); return PM3_EINVARG; } } if (res != PM3_SUCCESS) { free(data); return PM3_EFILE; } // 64 or 256blocks. if (bytes_read != (MIFARE_1K_MAXBLOCK * MFBLOCK_SIZE) && bytes_read != (MIFARE_4K_MAXBLOCK * MFBLOCK_SIZE)) { PrintAndLogEx(ERR, "File content error. Read %zu bytes", bytes_read); free(data); return PM3_EFILE; } PrintAndLogEx(INFO, "Copying to magic gen1a card"); PrintAndLogEx(INFO, "." NOLF); int blockno = 0; int flags = 0; while (bytes_read) { // switch on field and send magic sequence if (blockno == 0) { flags = MAGIC_INIT + MAGIC_WUPC; } // write if (blockno == 1) { flags = 0; } // switch off field if (blockno == MFBLOCK_SIZE * 4 - 1) { flags = MAGIC_HALT + MAGIC_OFF; } if (mfCSetBlock(blockno, data + (MFBLOCK_SIZE * blockno), NULL, flags)) { PrintAndLogEx(WARNING, "Can't set magic card block: %d", blockno); free(data); return PM3_ESOFT; } bytes_read -= MFBLOCK_SIZE; PrintAndLogEx(NORMAL, "." NOLF); fflush(stdout); blockno++; // magic card type - mifare 1K if (blockno >= MIFARE_1K_MAXBLOCK) break; } PrintAndLogEx(NORMAL, "\n"); free(data); // confirm number written blocks. Must be 64 or 256 blocks if (blockno != MIFARE_1K_MAXBLOCK) { if (blockno != MIFARE_4K_MAXBLOCK) { PrintAndLogEx(ERR, "File content error. There must be %u blocks", MIFARE_4K_MAXBLOCK); return PM3_EFILE; } PrintAndLogEx(ERR, "File content error. There must be %d blocks", MIFARE_1K_MAXBLOCK); return PM3_EFILE; } PrintAndLogEx(SUCCESS, "Card loaded %d blocks from file", blockno); PrintAndLogEx(INFO, "Done!"); return PM3_SUCCESS; } static int CmdHF14AMfCGetBlk(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mf cgetblk", "Get block data from magic Chinese card.\n" "Only works with magic gen1a cards", "hf mf cgetblk --blk 0 --> get block 0 (manufacturer)\n" "hf mf cgetblk --blk 3 -v --> get block 3, decode sector trailer\n" ); void *argtable[] = { arg_param_begin, arg_int1("b", "blk", "", "block number"), arg_lit0("v", "verbose", "verbose output"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); int b = arg_get_int_def(ctx, 1, 0); bool verbose = arg_get_lit(ctx, 2); CLIParserFree(ctx); if (b > 255) { return PM3_EINVARG; } uint8_t blockno = (uint8_t)b; uint8_t data[16] = {0}; int res = mfCGetBlock(blockno, data, MAGIC_SINGLE); if (res) { PrintAndLogEx(ERR, "Can't read block. error=%d", res); return PM3_ESOFT; } uint8_t sector = GetSectorFromBlockNo(blockno); mf_print_sector_hdr(sector); mf_print_block(blockno, data); if (verbose) { decode_print_st(blockno, data); } else { PrintAndLogEx(NORMAL, ""); } return PM3_SUCCESS; } static int CmdHF14AMfCGetSc(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mf cgetsc", "Get sector data from magic Chinese card.\n" "Only works with magic gen1a cards", "hf mf cgetsc -s 0" ); void *argtable[] = { arg_param_begin, arg_int1("s", "sec", "", "sector number"), arg_lit0("v", "verbose", "verbose output"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); int s = arg_get_int_def(ctx, 1, 0); bool verbose = arg_get_lit(ctx, 2); CLIParserFree(ctx); if (s > 39) { PrintAndLogEx(WARNING, "Sector number must be less then 40"); return PM3_EINVARG; } uint8_t sector = (uint8_t)s; mf_print_sector_hdr(sector); uint8_t blocks = 4; uint8_t start = sector * 4; if (sector >= 32) { blocks = 16; start = 128 + (sector - 32) * 16; } int flags = MAGIC_INIT + MAGIC_WUPC; uint8_t data[16] = {0}; for (int i = 0; i < blocks; i++) { if (i == 1) flags = 0; if (i == blocks - 1) flags = MAGIC_HALT + MAGIC_OFF; int res = mfCGetBlock(start + i, data, flags); if (res) { PrintAndLogEx(ERR, "Can't read block. %d error=%d", start + i, res); return PM3_ESOFT; } mf_print_block(start + i, data); } if (verbose) { decode_print_st(start + blocks - 1, data); } else { PrintAndLogEx(NORMAL, ""); } return PM3_SUCCESS; } static int CmdHF14AMfCSave(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mf csave", "Save magic gen1a card memory into three files (BIN/EML/JSON)" "or into emulator memory", "hf mf csave\n" "hf mf csave --4k" ); void *argtable[] = { arg_param_begin, arg_str0("f", "file", "", "filename of dump"), 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(NULL, "emu", "from emulator memory"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); int fnlen = 0; char filename[FILE_PATH_SIZE]; CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); bool m0 = arg_get_lit(ctx, 2); bool m1 = arg_get_lit(ctx, 3); bool m2 = arg_get_lit(ctx, 4); bool m4 = arg_get_lit(ctx, 5); bool fill_emulator = arg_get_lit(ctx, 6); CLIParserFree(ctx); // 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; } char s[6]; memset(s, 0, sizeof(s)); uint16_t block_cnt = MIFARE_1K_MAXBLOCK; if (m0) { block_cnt = MIFARE_MINI_MAXBLOCK; strncpy(s, "Mini", 5); } else if (m1) { block_cnt = MIFARE_1K_MAXBLOCK; strncpy(s, "1K", 3); } else if (m2) { block_cnt = MIFARE_2K_MAXBLOCK; strncpy(s, "2K", 3); } else if (m4) { block_cnt = MIFARE_4K_MAXBLOCK; strncpy(s, "4K", 3); } else { PrintAndLogEx(WARNING, "Please specify a MIFARE Type"); return PM3_EINVARG; } PrintAndLogEx(SUCCESS, "Dumping magic Gen1a MIFARE Classic " _GREEN_("%s") " card memory", s); PrintAndLogEx(INFO, "." NOLF); // Select card to get UID/UIDLEN 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; } /* 0: couldn't read 1: OK, with ATS 2: OK, no ATS 3: proprietary Anticollision */ 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)); // reserve memory uint16_t bytes = block_cnt * MFBLOCK_SIZE; uint8_t *dump = calloc(bytes, sizeof(uint8_t)); if (dump == NULL) { PrintAndLogEx(WARNING, "Fail, cannot allocate memory"); return PM3_EMALLOC; } // switch on field and send magic sequence uint8_t flags = MAGIC_INIT + MAGIC_WUPC; for (uint16_t i = 0; i < block_cnt; i++) { // read if (i == 1) { flags = 0; } // switch off field if (i == block_cnt - 1) { flags = MAGIC_HALT + MAGIC_OFF; } if (mfCGetBlock(i, dump + (i * MFBLOCK_SIZE), flags)) { PrintAndLogEx(WARNING, "Can't get magic card block: %d", i); PrintAndLogEx(HINT, "Verify your card size, and try again or try another tag position"); free(dump); return PM3_ESOFT; } PrintAndLogEx(NORMAL, "." NOLF); fflush(stdout); } PrintAndLogEx(NORMAL, ""); if (fill_emulator) { PrintAndLogEx(INFO, "uploading to emulator memory"); PrintAndLogEx(INFO, "." NOLF); // fast push mode conn.block_after_ACK = true; for (int i = 0; i < block_cnt; i += 5) { if (i == block_cnt - 1) { // Disable fast mode on last packet conn.block_after_ACK = false; } if (mfEmlSetMem(dump + (i * MFBLOCK_SIZE), i, 5) != PM3_SUCCESS) { PrintAndLogEx(WARNING, "Can't set emul block: %d", i); } PrintAndLogEx(NORMAL, "." NOLF); fflush(stdout); } PrintAndLogEx(NORMAL, ""); PrintAndLogEx(SUCCESS, "uploaded %d bytes to emulator memory", bytes); } // user supplied filename? if (fnlen < 1) { char *fptr = filename; fptr += snprintf(fptr, sizeof(filename), "hf-mf-"); FillFileNameByUID(fptr, card.uid, "-dump", card.uidlen); } saveFile(filename, ".bin", dump, bytes); saveFileEML(filename, dump, bytes, MFBLOCK_SIZE); saveFileJSON(filename, jsfCardMemory, dump, bytes, NULL); free(dump); return PM3_SUCCESS; } static int CmdHF14AMfCView(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mf cview", "View `magic gen1a` card memory", "hf mf cview\n" "hf mf cview --4k" ); void *argtable[] = { arg_param_begin, 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); bool m0 = arg_get_lit(ctx, 1); bool m1 = arg_get_lit(ctx, 2); bool m2 = arg_get_lit(ctx, 3); bool m4 = arg_get_lit(ctx, 4); CLIParserFree(ctx); // 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; } char s[6]; memset(s, 0, sizeof(s)); uint16_t block_cnt = MIFARE_1K_MAXBLOCK; if (m0) { block_cnt = MIFARE_MINI_MAXBLOCK; strncpy(s, "Mini", 5); } else if (m1) { block_cnt = MIFARE_1K_MAXBLOCK; strncpy(s, "1K", 3); } else if (m2) { block_cnt = MIFARE_2K_MAXBLOCK; strncpy(s, "2K", 3); } else if (m4) { block_cnt = MIFARE_4K_MAXBLOCK; strncpy(s, "4K", 3); } else { PrintAndLogEx(WARNING, "Please specify a MIFARE Type"); return PM3_EINVARG; } PrintAndLogEx(SUCCESS, "View magic Gen1a MIFARE Classic " _GREEN_("%s"), s); PrintAndLogEx(INFO, "." NOLF); // Select card to get UID/UIDLEN 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; } /* 0: couldn't read 1: OK, with ATS 2: OK, no ATS 3: proprietary Anticollision */ uint64_t select_status = resp.oldarg[0]; if (select_status == 0) { PrintAndLogEx(WARNING, "iso14443a card select failed"); return select_status; } iso14a_card_select_t card; memcpy(&card, (iso14a_card_select_t *)resp.data.asBytes, sizeof(iso14a_card_select_t)); // reserve memory uint16_t bytes = block_cnt * MFBLOCK_SIZE; uint8_t *dump = calloc(bytes, sizeof(uint8_t)); if (dump == NULL) { PrintAndLogEx(WARNING, "Fail, cannot allocate memory"); return PM3_EMALLOC; } // switch on field and send magic sequence uint8_t flags = MAGIC_INIT + MAGIC_WUPC; for (uint16_t i = 0; i < block_cnt; i++) { // read if (i == 1) { flags = 0; } // switch off field if (i == block_cnt - 1) { flags = MAGIC_HALT + MAGIC_OFF; } if (mfCGetBlock(i, dump + (i * MFBLOCK_SIZE), flags)) { PrintAndLogEx(WARNING, "Can't get magic card block: %u", i); PrintAndLogEx(HINT, "Verify your card size, and try again or try another tag position"); free(dump); return PM3_ESOFT; } PrintAndLogEx(NORMAL, "." NOLF); fflush(stdout); } PrintAndLogEx(NORMAL, ""); mf_print_blocks(block_cnt, dump); free(dump); return PM3_SUCCESS; } //needs nt, ar, at, Data to decrypt static int CmdHf14AMfDecryptBytes(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mf decrypt", "Decrypt Crypto-1 encrypted bytes given some known state of crypto. See tracelog to gather needed values", "hf mf decrypt --nt b830049b --ar 9248314a --at 9280e203 -d 41e586f9\n" " -> 41e586f9 becomes 3003999a\n" " -> which annotates 30 03 [99 9a] read block 3 [crc]" ); void *argtable[] = { arg_param_begin, arg_str1(NULL, "nt", "", "tag nonce"), arg_str1(NULL, "ar", "", "ar_enc, encrypted reader response"), arg_str1(NULL, "at", "", "at_enc, encrypted tag response"), arg_str1("d", "data", "", "encrypted data, taken directly after at_enc and forward"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); uint32_t nt = 0; int res = arg_get_u32_hexstr_def(ctx, 1, 0, &nt); if (res != 1) { CLIParserFree(ctx); PrintAndLogEx(WARNING, "check `nt` parameter"); return PM3_EINVARG; } uint32_t ar_enc = 0; res = arg_get_u32_hexstr_def(ctx, 2, 0, &ar_enc); if (res != 1) { CLIParserFree(ctx); PrintAndLogEx(WARNING, "check `ar` parameter"); return PM3_EINVARG; } uint32_t at_enc = 0; res = arg_get_u32_hexstr_def(ctx, 3, 0, &at_enc); if (res != 1) { CLIParserFree(ctx); PrintAndLogEx(WARNING, "check `at` parameter"); return PM3_EINVARG; } int datalen = 0; uint8_t data[512] = {0x00}; CLIGetHexWithReturn(ctx, 4, data, &datalen); CLIParserFree(ctx); PrintAndLogEx(INFO, "nt....... %08X", nt); PrintAndLogEx(INFO, "ar enc... %08X", ar_enc); PrintAndLogEx(INFO, "at enc... %08X", at_enc); return tryDecryptWord(nt, ar_enc, at_enc, data, datalen); } static int CmdHf14AMfSetMod(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mf setmod", "Sets the load modulation strength of a MIFARE Classic EV1 card", "hf mf setmod -k ffffffffffff -0" ); void *argtable[] = { arg_param_begin, arg_lit0("0", NULL, "normal modulation"), arg_lit0("1", NULL, "strong modulation (def)"), arg_str0("k", "key", "", "key A, Sector 0, 6 hex bytes"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); bool m0 = arg_get_lit(ctx, 1); bool m1 = arg_get_lit(ctx, 2); int keylen = 0; uint8_t key[6] = {0}; CLIGetHexWithReturn(ctx, 3, key, &keylen); CLIParserFree(ctx); if (m0 + m1 > 1) { PrintAndLogEx(WARNING, "please select one modulation"); return PM3_EINVARG; } uint8_t data[7] = {0}; memcpy(data + 1, key, 6); if (m1) { data[0] = 1; } else { data[0] = 0; } clearCommandBuffer(); SendCommandNG(CMD_HF_MIFARE_SETMOD, data, sizeof(data)); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_HF_MIFARE_SETMOD, &resp, 1500) == false) { PrintAndLogEx(WARNING, "Command execute timeout"); return PM3_ETIMEOUT; } if (resp.status == PM3_SUCCESS) PrintAndLogEx(SUCCESS, "Change ( " _GREEN_("ok") " )"); else PrintAndLogEx(FAILED, "Change (" _GREEN_("fail") " )"); return resp.status; } // MIFARE NACK bug detection static int CmdHf14AMfNack(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mf nack", "Test a MIFARE Classic based card for the NACK bug", "hf mf nack" ); void *argtable[] = { arg_param_begin, arg_lit0("v", "verbose", "verbose output`"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); bool verbose = arg_get_lit(ctx, 1); CLIParserFree(ctx); if (verbose) PrintAndLogEx(INFO, "Started testing card for NACK bug. Press Enter to abort"); detect_classic_nackbug(verbose); return PM3_SUCCESS; } /* static int CmdHF14AMfice(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mf ice", "Collect MIFARE Classic nonces to file", "hf mf ice\n" "hf mf ice -f nonces.bin"); void *argtable[] = { arg_param_begin, arg_str0("f", "file", "", "filename of nonce dump"), arg_u64_0(NULL, "limit", "", "nonces to be collected"), 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); uint32_t limit = arg_get_u32_def(ctx, 2, 50000); CLIParserFree(ctx); // Validations char *fptr; if (filename[0] == '\0') { fptr = GenerateFilename("hf-mf-", "-nonces.bin"); if (fptr == NULL) return PM3_EFILE; strcpy(filename, fptr); free(fptr); } uint8_t blockNo = 0; uint8_t keyType = MF_KEY_A; uint8_t trgBlockNo = 0; uint8_t trgKeyType = MF_KEY_B; bool slow = false; bool initialize = true; bool acquisition_completed = false; uint32_t total_num_nonces = 0; PacketResponseNG resp; uint32_t part_limit = 3000; PrintAndLogEx(NORMAL, "Collecting "_YELLOW_("%u")" nonces \n", limit); FILE *fnonces = NULL; if ((fnonces = fopen(filename, "wb")) == NULL) { PrintAndLogEx(WARNING, "Could not create file " _YELLOW_("%s"), filename); return PM3_EFILE; } clearCommandBuffer(); uint64_t t1 = msclock(); do { if (kbd_enter_pressed()) { PrintAndLogEx(WARNING, "\naborted via keyboard!\n"); break; } uint32_t flags = 0; flags |= initialize ? 0x0001 : 0; flags |= slow ? 0x0002 : 0; clearCommandBuffer(); SendCommandMIX(CMD_HF_MIFARE_ACQ_NONCES, blockNo + keyType * 0x100, trgBlockNo + trgKeyType * 0x100, flags, NULL, 0); if (!WaitForResponseTimeout(CMD_ACK, &resp, 3000)) goto out; if (resp.oldarg[0]) goto out; uint32_t items = resp.oldarg[2]; fwrite(resp.data.asBytes, 1, items * 4, fnonces); fflush(fnonces); total_num_nonces += items; if (total_num_nonces > part_limit) { PrintAndLogEx(INFO, "Total nonces %u\n", total_num_nonces); part_limit += 3000; } acquisition_completed = (total_num_nonces > limit); initialize = false; } while (!acquisition_completed); out: PrintAndLogEx(SUCCESS, "time: %" PRIu64 " seconds\n", (msclock() - t1) / 1000); if (fnonces) { fflush(fnonces); fclose(fnonces); } clearCommandBuffer(); SendCommandMIX(CMD_HF_MIFARE_ACQ_NONCES, blockNo + keyType * 0x100, trgBlockNo + trgKeyType * 0x100, 4, NULL, 0); return PM3_SUCCESS; } */ static int CmdHF14AMfAuth4(const char *Cmd) { uint8_t keyn[20] = {0}; int keynlen = 0; uint8_t key[16] = {0}; int keylen = 0; CLIParserContext *ctx; CLIParserInit(&ctx, "hf mf auth4", "Executes AES authentication command in ISO14443-4", "hf mf auth4 4000 000102030405060708090a0b0c0d0e0f -> executes authentication\n" "hf mf auth4 9003 FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -> executes authentication\n"); void *argtable[] = { arg_param_begin, arg_str1(NULL, NULL, "", NULL), arg_str1(NULL, NULL, "", NULL), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); CLIGetHexWithReturn(ctx, 1, keyn, &keynlen); CLIGetHexWithReturn(ctx, 2, key, &keylen); CLIParserFree(ctx); if (keynlen != 2) { PrintAndLogEx(ERR, " must be 2 bytes long instead of: %d", keynlen); return PM3_ESOFT; } if (keylen != 16) { PrintAndLogEx(ERR, " must be 16 bytes long instead of: %d", keylen); return PM3_ESOFT; } return MifareAuth4(NULL, keyn, key, true, false, true, true, false); } // https://www.nxp.com/docs/en/application-note/AN10787.pdf static int CmdHF14AMfMAD(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mf mad", "Checks and prints MIFARE Application Directory (MAD)", "hf mf mad -> shows MAD if exists\n" "hf mf mad --aid e103 -k ffffffffffff -b -> shows NDEF data if exists. read card with custom key and key B\n" "hf mf mad --dch -k ffffffffffff -> decode CardHolder information\n"); void *argtable[] = { arg_param_begin, arg_lit0("v", "verbose", "show technical data"), arg_str0(NULL, "aid", "", "print all sectors with specified aid"), arg_str0("k", "key", "", "key for printing sectors"), arg_lit0("b", "keyb", "use key B for access printing sectors (by default: key A)"), arg_lit0(NULL, "be", "(optional, BigEndian)"), arg_lit0(NULL, "dch", "decode Card Holder information"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); bool verbose = arg_get_lit(ctx, 1); uint8_t aid[2] = {0}; int aidlen = 0; CLIGetHexWithReturn(ctx, 2, aid, &aidlen); uint8_t userkey[6] = {0}; int keylen = 0; CLIGetHexWithReturn(ctx, 3, userkey, &keylen); bool keyB = arg_get_lit(ctx, 4); bool swapmad = arg_get_lit(ctx, 5); bool decodeholder = arg_get_lit(ctx, 6); CLIParserFree(ctx); uint8_t sector0[16 * 4] = {0}; uint8_t sector10[16 * 4] = {0}; bool got_first = true; if (mfReadSector(MF_MAD1_SECTOR, MF_KEY_A, (uint8_t *)g_mifare_mad_key, sector0) != PM3_SUCCESS) { PrintAndLogEx(WARNING, "error, read sector 0. card don't have MAD or don't have MAD on default keys"); got_first = false; } else { PrintAndLogEx(INFO, "Authentication ( " _GREEN_("OK") " )"); } // User supplied key if (got_first == false && keylen == 6) { PrintAndLogEx(INFO, "Trying user specified key..."); if (mfReadSector(MF_MAD1_SECTOR, MF_KEY_A, userkey, sector0) != PM3_SUCCESS) { PrintAndLogEx(ERR, "error, read sector 0. card don't have MAD or don't the custom key is wrong"); } else { PrintAndLogEx(INFO, "Authentication ( " _GREEN_("OK") " )"); got_first = true; } } // Both default and user supplied key failed if (got_first == false) { return PM3_ESOFT; } PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "--- " _CYAN_("MIFARE App Directory Information") " ----------------"); PrintAndLogEx(INFO, "-----------------------------------------------------"); bool haveMAD2 = false; MAD1DecodeAndPrint(sector0, swapmad, verbose, &haveMAD2); if (haveMAD2) { if (mfReadSector(MF_MAD2_SECTOR, MF_KEY_A, (uint8_t *)g_mifare_mad_key, sector10)) { PrintAndLogEx(ERR, "error, read sector 0x10. card don't have MAD or don't have MAD on default keys"); return PM3_ESOFT; } MAD2DecodeAndPrint(sector10, swapmad, verbose); } if (aidlen == 2 || decodeholder) { uint16_t mad[7 + 8 + 8 + 8 + 8] = {0}; size_t madlen = 0; if (MADDecode(sector0, sector10, mad, &madlen, swapmad)) { PrintAndLogEx(ERR, "can't decode MAD"); return PM3_ESOFT; } // copy default NDEF key uint8_t akey[6] = {0}; memcpy(akey, g_mifare_ndef_key, 6); // user specified key if (keylen == 6) { memcpy(akey, userkey, 6); } uint16_t aaid = 0x0004; if (aidlen == 2) { aaid = (aid[0] << 8) + aid[1]; PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "-------------- " _CYAN_("AID 0x%04x") " ---------------", aaid); for (int i = 0; i < madlen; i++) { if (aaid == mad[i]) { uint8_t vsector[16 * 4] = {0}; if (mfReadSector(i + 1, keyB ? MF_KEY_B : MF_KEY_A, akey, vsector)) { PrintAndLogEx(NORMAL, ""); PrintAndLogEx(ERR, "error, read sector %d", i + 1); return PM3_ESOFT; } for (int j = 0; j < (verbose ? 4 : 3); j ++) PrintAndLogEx(NORMAL, " [%03d] %s", (i + 1) * 4 + j, sprint_hex(&vsector[j * 16], 16)); } } } if (decodeholder) { PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "-------- " _CYAN_("Card Holder Info 0x%04x") " --------", aaid); uint8_t data[4096] = {0}; int datalen = 0; for (int i = 0; i < madlen; i++) { if (aaid == mad[i]) { uint8_t vsector[16 * 4] = {0}; if (mfReadSector(i + 1, keyB ? MF_KEY_B : MF_KEY_A, akey, vsector)) { PrintAndLogEx(NORMAL, ""); PrintAndLogEx(ERR, "error, read sector %d", i + 1); return PM3_ESOFT; } memcpy(&data[datalen], vsector, 16 * 3); datalen += 16 * 3; } } if (!datalen) { PrintAndLogEx(WARNING, "no Card Holder Info data"); return PM3_SUCCESS; } MADCardHolderInfoDecode(data, datalen, verbose); } } if (verbose) { PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "------------ " _CYAN_("MAD sector raw") " -------------"); for (int i = 0; i < 4; i ++) PrintAndLogEx(INFO, "[%d] %s", i, sprint_hex(§or0[i * 16], 16)); } return PM3_SUCCESS; } static int CmdHFMFNDEF(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mf ndef", "Prints NFC Data Exchange Format (NDEF)", "hf mf ndef -> shows NDEF parsed data\n" "hf mf ndef -vv -> shows NDEF parsed and raw data\n" "hf mf ndef --aid e103 -k ffffffffffff -b -> shows NDEF data with custom AID, key and with key B\n"); void *argtable[] = { arg_param_begin, arg_litn("v", "verbose", 0, 2, "show technical data"), arg_str0(NULL, "aid", "", "replace default aid for NDEF"), arg_str0("k", "key", "", "replace default key for NDEF"), arg_lit0("b", "keyb", "use key B for access sectors (by default: key A)"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); bool verbose = arg_get_lit(ctx, 1); bool verbose2 = arg_get_lit(ctx, 1) > 1; uint8_t aid[2] = {0}; int aidlen; CLIGetHexWithReturn(ctx, 2, aid, &aidlen); uint8_t key[6] = {0}; int keylen; CLIGetHexWithReturn(ctx, 3, key, &keylen); bool keyB = arg_get_lit(ctx, 4); CLIParserFree(ctx); uint16_t ndefAID = 0xe103; if (aidlen == 2) ndefAID = (aid[0] << 8) + aid[1]; uint8_t ndefkey[6] = {0}; memcpy(ndefkey, g_mifare_ndef_key, 6); if (keylen == 6) { memcpy(ndefkey, key, 6); } uint8_t sector0[16 * 4] = {0}; uint8_t sector10[16 * 4] = {0}; uint8_t data[4096] = {0}; int datalen = 0; if (verbose) PrintAndLogEx(INFO, "reading MAD v1 sector"); if (mfReadSector(MF_MAD1_SECTOR, MF_KEY_A, (uint8_t *)g_mifare_mad_key, sector0)) { PrintAndLogEx(ERR, "error, read sector 0. card don't have MAD or don't have MAD on default keys"); PrintAndLogEx(HINT, "Try " _YELLOW_("`hf mf ndef -k `") " with your custom key"); return PM3_ESOFT; } bool haveMAD2 = false; int res = MADCheck(sector0, NULL, verbose, &haveMAD2); if (res != PM3_SUCCESS) { PrintAndLogEx(ERR, "MAD error %d", res); return res; } if (haveMAD2) { if (verbose) PrintAndLogEx(INFO, "reading MAD v2 sector"); if (mfReadSector(MF_MAD2_SECTOR, MF_KEY_A, (uint8_t *)g_mifare_mad_key, sector10)) { PrintAndLogEx(ERR, "error, read sector 0x10. card don't have MAD or don't have MAD on default keys"); PrintAndLogEx(HINT, "Try " _YELLOW_("`hf mf ndef -k `") " with your custom key"); return PM3_ESOFT; } } uint16_t mad[7 + 8 + 8 + 8 + 8] = {0}; size_t madlen = 0; res = MADDecode(sector0, (haveMAD2 ? sector10 : NULL), mad, &madlen, false); if (res != PM3_SUCCESS) { PrintAndLogEx(ERR, "can't decode MAD"); return res; } PrintAndLogEx(INFO, "reading data from tag"); for (int i = 0; i < madlen; i++) { if (ndefAID == mad[i]) { uint8_t vsector[16 * 4] = {0}; if (mfReadSector(i + 1, keyB ? MF_KEY_B : MF_KEY_A, ndefkey, vsector)) { PrintAndLogEx(ERR, "error, reading sector %d ", i + 1); return PM3_ESOFT; } memcpy(&data[datalen], vsector, 16 * 3); datalen += 16 * 3; PrintAndLogEx(INPLACE, "%d", i); } } PrintAndLogEx(NORMAL, ""); if (!datalen) { PrintAndLogEx(WARNING, "no NDEF data"); return PM3_SUCCESS; } if (verbose2) { PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "--- " _CYAN_("MFC NDEF raw") " ----------------"); print_buffer(data, datalen, 1); } NDEFDecodeAndPrint(data, datalen, verbose); PrintAndLogEx(HINT, "Try " _YELLOW_("`hf mf ndef -vv`") " for more details"); return PM3_SUCCESS; } static int CmdHFMFPersonalize(const char *cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mf personalize", "Personalize the UID of a MIFARE Classic EV1 card. This is only possible \n" "if it is a 7Byte UID card and if it is not already personalized.", "hf mf personalize --f0 -> double size UID\n" "hf mf personalize --f1 -> double size UID, optional usage of selection process shortcut\n" "hf mf personalize --f2 -> single size random ID\n" "hf mf personalize --f3 -> single size NUID\n" "hf mf personalize -b -k B0B1B2B3B4B5 --f3 -> use key B = 0xB0B1B2B3B4B5" ); void *argtable[] = { arg_param_begin, arg_lit0("a", NULL, "use key A to authenticate sector 0 (def)"), arg_lit0("b", NULL, "use key B to authenticate sector 0"), arg_str0("k", "key", "", "key (def FFFFFFFFFFFF)"), arg_lit0(NULL, "f0", "UIDFO, double size UID"), arg_lit0(NULL, "f1", "UIDF1, double size UID, optional usage of selection process shortcut"), arg_lit0(NULL, "f2", "UIDF2, single size random ID"), arg_lit0(NULL, "f3", "UIDF3, single size NUID"), arg_param_end }; CLIExecWithReturn(ctx, cmd, argtable, true); bool use_a = arg_get_lit(ctx, 1); bool use_b = arg_get_lit(ctx, 2); if (use_a + use_b > 1) { PrintAndLogEx(ERR, "error, use only one key type"); CLIParserFree(ctx); return PM3_EINVARG; } uint8_t keytype = 0; if (use_b) { keytype = 1; } uint8_t key[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; int key_len; int res = CLIParamHexToBuf(arg_get_str(ctx, 3), key, 6, &key_len); if (res || (!res && key_len && key_len != 6)) { PrintAndLogEx(ERR, "ERROR: not a valid key. Key must be 12 hex digits"); CLIParserFree(ctx); return PM3_EINVARG; } bool f0 = arg_get_lit(ctx, 4); bool f1 = arg_get_lit(ctx, 5); bool f2 = arg_get_lit(ctx, 6); bool f3 = arg_get_lit(ctx, 7); CLIParserFree(ctx); uint8_t tmp = f0 + f1 + f2 + f3; if (tmp > 1) { PrintAndLogEx(WARNING, "select only one key type"); return PM3_EINVARG; } if (tmp == 0) { PrintAndLogEx(WARNING, "select one key type"); return PM3_EINVARG; } uint8_t pers_option = MIFARE_EV1_UIDF3; if (f0) { pers_option = MIFARE_EV1_UIDF0; } else if (f1) { pers_option = MIFARE_EV1_UIDF1; } else if (f2) { pers_option = MIFARE_EV1_UIDF2; } CLIParserFree(ctx); struct { uint8_t keytype; uint8_t pers_option; uint8_t key[6]; } PACKED payload; payload.keytype = keytype; payload.pers_option = pers_option; memcpy(payload.key, key, sizeof(payload.key)); clearCommandBuffer(); SendCommandNG(CMD_HF_MIFARE_PERSONALIZE_UID, (uint8_t *)&payload, sizeof(payload)); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_HF_MIFARE_PERSONALIZE_UID, &resp, 2500) == false) { return PM3_ETIMEOUT; } if (resp.status == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, "Personalization ( %s )", _GREEN_("ok")); } else { PrintAndLogEx(FAILED, "Personalization ( %s )", _RED_("fail")); } return PM3_SUCCESS; } static int CmdHF14AMfList(const char *Cmd) { return CmdTraceListAlias(Cmd, "hf mf", "mf"); } static int CmdHf14AGen3UID(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mf gen3uid", "Set UID for magic Gen3 card _without_ changes to manufacturer block 0", "hf mf gen3uid --uid 01020304 --> set 4 byte uid\n" "hf mf gen3uid --uid 01020304050607 --> set 7 byte uid" ); void *argtable[] = { arg_param_begin, arg_str0("u", "uid", "", "UID 4/7 hex bytes"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); uint8_t uid[7] = {0}; int uidlen = 0; CLIGetHexWithReturn(ctx, 1, uid, &uidlen); CLIParserFree(ctx); // sanity checks if (uidlen != 4 && uidlen != 7) { PrintAndLogEx(FAILED, "UID must be 4 or 7 hex bytes. Got %d", uidlen); return PM3_EINVARG; } uint8_t old_uid[10] = {0}; int res = mfGen3UID(uid, uidlen, old_uid); if (res != PM3_SUCCESS) { PrintAndLogEx(ERR, "Can't set UID"); PrintAndLogEx(HINT, "Are you sure your card is a Gen3 ?"); return PM3_ESOFT; } PrintAndLogEx(SUCCESS, "Old UID... %s", sprint_hex(old_uid, uidlen)); PrintAndLogEx(SUCCESS, "New UID... %s", sprint_hex(uid, uidlen)); return PM3_SUCCESS; } static int CmdHf14AGen3Block(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mf gen3blk", "Overwrite full manufacturer block for magic Gen3 card\n" " - You can specify part of manufacturer block as\n" " 4/7-bytes for UID change only\n" "\n" "NOTE: BCC, SAK, ATQA will be calculated automatically" , "hf mf gen3blk --> print current data\n" "hf mf gen3blk -d 01020304 --> set 4 byte uid\n" "hf mf gen3blk -d 01020304050607 --> set 7 byte uid \n" "hf mf gen3blk -d 01020304FFFFFFFF0102030405060708" ); void *argtable[] = { arg_param_begin, arg_str0("d", "data", "", "manufacturer block data up to 16 hex bytes"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); uint8_t data[MFBLOCK_SIZE] = {0x00}; int datalen = 0; CLIGetHexWithReturn(ctx, 1, data, &datalen); CLIParserFree(ctx); uint8_t new_block[MFBLOCK_SIZE] = {0x00}; int res = mfGen3Block(data, datalen, new_block); if (res) { PrintAndLogEx(ERR, "Can't change manufacturer block data. error %d", res); return PM3_ESOFT; } PrintAndLogEx(SUCCESS, "Current block... %s", sprint_hex_inrow(new_block, sizeof(new_block))); return PM3_SUCCESS; } static int CmdHf14AGen3Freeze(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mf gen3freeze", "Perma lock further UID changes. No more UID changes available after operation completed\n" "\nNote: operation is " _RED_("! irreversible !"), "hf mf gen3freeze -y" ); void *argtable[] = { arg_param_begin, arg_lit1("y", "yes", "confirm UID lock operation"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); bool confirm = arg_get_lit(ctx, 1); CLIParserFree(ctx); if (confirm == false) { PrintAndLogEx(INFO, "please confirm that you want to perma lock the card"); return PM3_SUCCESS; } int res = mfGen3Freeze(); if (res != PM3_SUCCESS) { PrintAndLogEx(ERR, "Can't lock UID changes. error %d", res); } else { PrintAndLogEx(SUCCESS, "MFC Gen3 UID card is now perma-locked"); } return res; } static void des_decrypt(void *out, const void *in, const void *key) { mbedtls_des_context ctx; mbedtls_des_setkey_dec(&ctx, key); mbedtls_des_crypt_ecb(&ctx, in, out); } static int CmdHf14AMfSuperCard(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mf supercard", "Extract info from a `super card`", "hf mf supercard"); void *argtable[] = { arg_param_begin, arg_lit0("r", "reset", "reset card"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); bool reset_card = arg_get_lit(ctx, 1); CLIParserFree(ctx); bool activate_field = true; bool keep_field_on = true; int res = 0; if (reset_card) { keep_field_on = false; uint8_t response[6]; int resplen = 0; // --------------- RESET CARD ---------------- uint8_t aRESET[] = { 0x00, 0xa6, 0xc0, 0x00 }; res = ExchangeAPDU14a(aRESET, sizeof(aRESET), activate_field, keep_field_on, response, sizeof(response), &resplen); if (res != PM3_SUCCESS) { PrintAndLogEx(FAILED, "Super card reset [ " _RED_("fail") " ]"); DropField(); return res; } PrintAndLogEx(SUCCESS, "Super card reset [ " _GREEN_("ok") " ]"); return PM3_SUCCESS; } uint8_t responseA[22]; uint8_t responseB[22]; int respAlen = 0; int respBlen = 0; // --------------- First ---------------- uint8_t aFIRST[] = { 0x00, 0xa6, 0xb0, 0x00, 0x10 }; res = ExchangeAPDU14a(aFIRST, sizeof(aFIRST), activate_field, keep_field_on, responseA, sizeof(responseA), &respAlen); if (res != PM3_SUCCESS) { DropField(); return res; } // --------------- Second ---------------- activate_field = false; keep_field_on = false; uint8_t aSECOND[] = { 0x00, 0xa6, 0xb0, 0x01, 0x10 }; res = ExchangeAPDU14a(aSECOND, sizeof(aSECOND), activate_field, keep_field_on, responseB, sizeof(responseB), &respBlen); if (res != PM3_SUCCESS) { DropField(); return res; } // uint8_t inA[] = { 0x72, 0xD7, 0xF4, 0x3E, 0xFD, 0xAB, 0xF2, 0x35, 0xFD, 0x49, 0xEE, 0xDC, 0x44, 0x95, 0x43, 0xC4}; // uint8_t inB[] = { 0xF0, 0xA2, 0x67, 0x6A, 0x04, 0x6A, 0x72, 0x12, 0x76, 0xA4, 0x1D, 0x02, 0x1F, 0xEA, 0x20, 0x85}; uint8_t outA[16] = {0}; uint8_t outB[16] = {0}; uint8_t key[] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}; for (uint8_t i = 0; i < 16; i += 8) { des_decrypt(outA + i, responseA + i, key); des_decrypt(outB + i, responseB + i, key); } PrintAndLogEx(DEBUG, " in : %s", sprint_hex_inrow(responseA, respAlen)); PrintAndLogEx(DEBUG, "out : %s", sprint_hex_inrow(outA, sizeof(outA))); PrintAndLogEx(DEBUG, " in : %s", sprint_hex_inrow(responseB, respAlen)); PrintAndLogEx(DEBUG, "out : %s", sprint_hex_inrow(outB, sizeof(outB))); if (memcmp(outA, "\x01\x01\x01\x01\x01\x01\x01\x01", 8) == 0) { PrintAndLogEx(INFO, "No trace recorded"); return PM3_SUCCESS; } // second trace? if (memcmp(outB, "\x01\x01\x01\x01\x01\x01\x01\x01", 8) == 0) { PrintAndLogEx(INFO, "Only one trace recorded"); return PM3_SUCCESS; } nonces_t data; // first uint16_t NT0 = (outA[6] << 8) | outA[7]; data.cuid = bytes_to_num(outA, 4); data.nonce = prng_successor(NT0, 31); data.nr = bytes_to_num(outA + 8, 4); data.ar = bytes_to_num(outA + 12, 4); data.at = 0; // second NT0 = (outB[6] << 8) | outB[7]; data.nonce2 = prng_successor(NT0, 31);; data.nr2 = bytes_to_num(outB + 8, 4); data.ar2 = bytes_to_num(outB + 12, 4); data.sector = GetSectorFromBlockNo(outA[5]); data.keytype = outA[4]; data.state = FIRST; PrintAndLogEx(DEBUG, "A Sector %02x", data.sector); PrintAndLogEx(DEBUG, "A NT %08x", data.nonce); PrintAndLogEx(DEBUG, "A NR %08x", data.nr); PrintAndLogEx(DEBUG, "A AR %08x", data.ar); PrintAndLogEx(DEBUG, ""); PrintAndLogEx(DEBUG, "B NT %08x", data.nonce2); PrintAndLogEx(DEBUG, "B NR %08x", data.nr2); PrintAndLogEx(DEBUG, "B AR %08x", data.ar2); uint64_t key64 = -1; res = mfkey32_moebius(&data, &key64); if (res) { PrintAndLogEx(SUCCESS, "UID: %s Sector %02x key %c [ " _GREEN_("%12" PRIX64) " ]" , sprint_hex_inrow(outA, 4) , data.sector , (data.keytype == 0x60) ? 'A' : 'B' , key64); } else { PrintAndLogEx(FAILED, "failed to recover any key"); } return PM3_SUCCESS; } static int CmdHF14AMfWipe(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mf wipe", "Wipe card to zeros and default keys/acc. This command takes a key file to wipe card\n" "Will use UID from card to generate keyfile name if not specified.\n" "New A/B keys..... FF FF FF FF FF FF\n" "New acc rights... FF 07 80\n" "New GPB.......... 69", "hf mf wipe --> reads card uid to generate file name\n" "hf mf wipe --gen2 --> force write to S0, B0 manufacture block\n" "hf mf wipe -f mykey.bin --> use mykey.bin\n" ); void *argtable[] = { arg_param_begin, arg_str0("f", "file", "", "key filename"), arg_lit0(NULL, "gen2", "force write to Sector 0, block 0 (GEN2)"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); int keyfnlen = 0; char keyFilename[FILE_PATH_SIZE] = {0}; CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)keyFilename, FILE_PATH_SIZE, &keyfnlen); bool gen2 = arg_get_lit(ctx, 2); CLIParserFree(ctx); char *fptr; if (keyfnlen == 0) { fptr = GenerateFilename("hf-mf-", "-key.bin"); if (fptr == NULL) return PM3_ESOFT; strcpy(keyFilename, fptr); free(fptr); } uint8_t *keys; size_t keyslen = 0; if (loadFile_safeEx(keyFilename, ".bin", (void **)&keys, (size_t *)&keyslen, false) != PM3_SUCCESS) { PrintAndLogEx(FAILED, "failed to load key file"); return PM3_ESOFT; } uint8_t keyA[MIFARE_4K_MAXSECTOR * 6]; uint8_t keyB[MIFARE_4K_MAXSECTOR * 6]; uint8_t num_sectors = 0; uint8_t mf[MFBLOCK_SIZE]; switch (keyslen) { case (MIFARE_MINI_MAXSECTOR * 2 * 6): { PrintAndLogEx(INFO, "Loaded keys matching MIFARE Classic Mini 320b"); memcpy(keyA, keys, (MIFARE_MINI_MAXSECTOR * 6)); memcpy(keyB, keys + (MIFARE_MINI_MAXSECTOR * 6), (MIFARE_MINI_MAXSECTOR * 6)); num_sectors = NumOfSectors('0'); memcpy(mf, "\x11\x22\x33\x44\x44\x09\x04\x00\x62\x63\x64\x65\x66\x67\x68\x69", MFBLOCK_SIZE); break; } case (MIFARE_1K_MAXSECTOR * 2 * 6): { PrintAndLogEx(INFO, "Loaded keys matching MIFARE Classic 1K"); memcpy(keyA, keys, (MIFARE_1K_MAXSECTOR * 6)); memcpy(keyB, keys + (MIFARE_1K_MAXSECTOR * 6), (MIFARE_1K_MAXSECTOR * 6)); num_sectors = NumOfSectors('1'); memcpy(mf, "\x11\x22\x33\x44\x44\x08\x04\x00\x62\x63\x64\x65\x66\x67\x68\x69", MFBLOCK_SIZE); break; } case (MIFARE_4K_MAXSECTOR * 2 * 6): { PrintAndLogEx(INFO, "Loaded keys matching MIFARE Classic 4K"); memcpy(keyA, keys, (MIFARE_4K_MAXSECTOR * 6)); memcpy(keyB, keys + (MIFARE_4K_MAXSECTOR * 6), (MIFARE_4K_MAXSECTOR * 6)); num_sectors = NumOfSectors('4'); memcpy(mf, "\x11\x22\x33\x44\x44\x18\x02\x00\x62\x63\x64\x65\x66\x67\x68\x69", MFBLOCK_SIZE); break; } default: { PrintAndLogEx(INFO, "wrong key file size"); goto out; } } if (gen2) PrintAndLogEx(INFO, "Forcing overwrite of sector 0 / block 0 "); else PrintAndLogEx(INFO, "Skipping sector 0 / block 0"); PrintAndLogEx(NORMAL, ""); uint8_t zeros[MFBLOCK_SIZE] = {0}; memset(zeros, 0x00, sizeof(zeros)); uint8_t st[MFBLOCK_SIZE] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x07, 0x80, 0x69, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; // time to wipe card for (uint8_t s = 0; s < num_sectors; s++) { for (uint8_t b = 0; b < NumBlocksPerSector(s); b++) { // Skipp write to manufacture block if not enforced if (s == 0 && b == 0 && gen2 == false) { continue; } uint8_t data[26]; memset(data, 0, sizeof(data)); if (mfIsSectorTrailer(b)) { memcpy(data + 10, st, sizeof(st)); } else { memcpy(data + 10, zeros, sizeof(zeros)); } // add correct manufacture block if UID Gen2 if (s == 0 && b == 0 && gen2) { memcpy(data + 10, mf, sizeof(mf)); } // try both A/B keys, start with B key first for (int8_t kt = MF_KEY_B; kt > -1; kt--) { if (kt == MF_KEY_A) memcpy(data, keyA + (s * 6), 6); else memcpy(data, keyB + (s * 6), 6); PrintAndLogEx(INFO, "block %3d: %s" NOLF, FirstBlockOfSector(s) + b, sprint_hex(data + 10, MFBLOCK_SIZE)); clearCommandBuffer(); SendCommandMIX(CMD_HF_MIFARE_WRITEBL, FirstBlockOfSector(s) + b, kt, 0, data, sizeof(data)); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { uint8_t isOK = resp.oldarg[0] & 0xff; if (isOK == 0) { PrintAndLogEx(NORMAL, "( " _RED_("fail") " )"); } else { PrintAndLogEx(NORMAL, "( " _GREEN_("ok") " )"); break; } } else { PrintAndLogEx(WARNING, "Command execute timeout"); } } } } PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "Done!"); out: free(keys); return PM3_SUCCESS; } 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" ); void *argtable[] = { arg_param_begin, arg_str1("f", "file", "", "filename of dump"), 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); CLIParserFree(ctx); // reserve memory uint8_t *dump = NULL; size_t bytes_read = 0; int res = 0; DumpFileType_t dftype = getfiletype(filename); switch (dftype) { case BIN: { res = loadFile_safe(filename, ".bin", (void **)&dump, &bytes_read); break; } case EML: { res = loadFileEML_safe(filename, (void **)&dump, &bytes_read); break; } case JSON: { dump = calloc(MFBLOCK_SIZE * MIFARE_4K_MAXBLOCK, sizeof(uint8_t)); if (dump == NULL) { PrintAndLogEx(WARNING, "Fail, cannot allocate memory"); return PM3_EMALLOC; } res = loadFileJSON(filename, (void *)dump, MIFARE_4K_MAXBLOCK * MFBLOCK_SIZE, &bytes_read, NULL); break; } case DICTIONARY: { PrintAndLogEx(ERR, "Error: Only BIN/JSON/EML formats allowed"); free(dump); return PM3_EINVARG; } } if (res != PM3_SUCCESS) { PrintAndLogEx(FAILED, "File: " _YELLOW_("%s") ": not found or locked.", filename); free(dump); return PM3_EFILE; } 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 (verbose) { PrintAndLogEx(INFO, "File: " _YELLOW_("%s"), filename); PrintAndLogEx(INFO, "File size %zu bytes, file blocks %d (0x%x)", bytes_read, block_cnt, block_cnt); } mf_print_blocks(block_cnt, dump); free(dump); return PM3_SUCCESS; } static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help"}, {"list", CmdHF14AMfList, AlwaysAvailable, "List MIFARE history"}, {"-----------", CmdHelp, IfPm3Iso14443a, "----------------------- " _CYAN_("recovery") " -----------------------"}, {"darkside", CmdHF14AMfDarkside, IfPm3Iso14443a, "Darkside attack"}, {"nested", CmdHF14AMfNested, IfPm3Iso14443a, "Nested attack"}, {"hardnested", CmdHF14AMfNestedHard, AlwaysAvailable, "Nested attack for hardened MIFARE Classic cards"}, {"staticnested", CmdHF14AMfNestedStatic, IfPm3Iso14443a, "Nested attack against static nonce MIFARE Classic cards"}, {"autopwn", CmdHF14AMfAutoPWN, IfPm3Iso14443a, "Automatic key recovery tool for MIFARE Classic"}, // {"keybrute", CmdHF14AMfKeyBrute, IfPm3Iso14443a, "J_Run's 2nd phase of multiple sector nested authentication key recovery"}, {"nack", CmdHf14AMfNack, IfPm3Iso14443a, "Test for MIFARE NACK bug"}, {"chk", CmdHF14AMfChk, IfPm3Iso14443a, "Check keys"}, {"fchk", CmdHF14AMfChk_fast, IfPm3Iso14443a, "Check keys fast, targets all keys on card"}, {"decrypt", CmdHf14AMfDecryptBytes, AlwaysAvailable, "[nt] [ar_enc] [at_enc] [data] - to decrypt sniff or trace"}, {"supercard", CmdHf14AMfSuperCard, IfPm3Iso14443a, "Extract info from a `super card`"}, {"-----------", CmdHelp, IfPm3Iso14443a, "----------------------- " _CYAN_("operations") " -----------------------"}, {"auth4", CmdHF14AMfAuth4, IfPm3Iso14443a, "ISO14443-4 AES authentication"}, {"dump", CmdHF14AMfDump, IfPm3Iso14443a, "Dump MIFARE Classic tag to binary file"}, {"mad", CmdHF14AMfMAD, IfPm3Iso14443a, "Checks and prints MAD"}, {"ndef", CmdHFMFNDEF, IfPm3Iso14443a, "Prints NDEF records from card"}, {"personalize", CmdHFMFPersonalize, IfPm3Iso14443a, "Personalize UID (MIFARE Classic EV1 only)"}, {"rdbl", CmdHF14AMfRdBl, IfPm3Iso14443a, "Read MIFARE Classic block"}, {"rdsc", CmdHF14AMfRdSc, IfPm3Iso14443a, "Read MIFARE Classic sector"}, {"restore", CmdHF14AMfRestore, IfPm3Iso14443a, "Restore MIFARE Classic binary file to BLANK tag"}, {"setmod", CmdHf14AMfSetMod, IfPm3Iso14443a, "Set MIFARE Classic EV1 load modulation strength"}, {"view", CmdHF14AMfView, AlwaysAvailable, "Display content from tag dump file"}, {"wipe", CmdHF14AMfWipe, IfPm3Iso14443a, "Wipe card to zeros and default keys/acc"}, {"wrbl", CmdHF14AMfWrBl, IfPm3Iso14443a, "Write MIFARE Classic block"}, {"-----------", CmdHelp, IfPm3Iso14443a, "----------------------- " _CYAN_("simulation") " -----------------------"}, {"sim", CmdHF14AMfSim, IfPm3Iso14443a, "Simulate MIFARE card"}, {"ecfill", CmdHF14AMfECFill, IfPm3Iso14443a, "Fill emulator memory with help of keys from emulator"}, {"eclr", CmdHF14AMfEClear, IfPm3Iso14443a, "Clear emulator memory"}, {"egetblk", CmdHF14AMfEGetBlk, IfPm3Iso14443a, "Get emulator memory block"}, {"egetsc", CmdHF14AMfEGetSc, IfPm3Iso14443a, "Get emulator memory sector"}, {"ekeyprn", CmdHF14AMfEKeyPrn, IfPm3Iso14443a, "Print keys from emulator memory"}, {"eload", CmdHF14AMfELoad, IfPm3Iso14443a, "Load from file emul dump"}, {"esave", CmdHF14AMfESave, IfPm3Iso14443a, "Save to file emul dump"}, {"esetblk", CmdHF14AMfESet, IfPm3Iso14443a, "Set emulator memory block"}, {"eview", CmdHF14AMfEView, IfPm3Iso14443a, "View emulator memory"}, {"-----------", CmdHelp, IfPm3Iso14443a, "----------------------- " _CYAN_("magic gen1") " -----------------------"}, {"cgetblk", CmdHF14AMfCGetBlk, IfPm3Iso14443a, "Read block from card"}, {"cgetsc", CmdHF14AMfCGetSc, IfPm3Iso14443a, "Read sector from card"}, {"cload", CmdHF14AMfCLoad, IfPm3Iso14443a, "Load dump to card"}, {"csave", CmdHF14AMfCSave, IfPm3Iso14443a, "Save dump from card into file or emulator"}, {"csetblk", CmdHF14AMfCSetBlk, IfPm3Iso14443a, "Write block to card"}, {"csetuid", CmdHF14AMfCSetUID, IfPm3Iso14443a, "Set UID on card"}, {"cview", CmdHF14AMfCView, IfPm3Iso14443a, "View card"}, {"cwipe", CmdHF14AMfCWipe, IfPm3Iso14443a, "Wipe card to default UID/Sectors/Keys"}, {"-----------", CmdHelp, IfPm3Iso14443a, "----------------------- " _CYAN_("magic gen3") " -----------------------"}, {"gen3uid", CmdHf14AGen3UID, IfPm3Iso14443a, "Set UID without changing manufacturer block"}, {"gen3blk", CmdHf14AGen3Block, IfPm3Iso14443a, "Overwrite manufacturer block"}, {"gen3freeze", CmdHf14AGen3Freeze, IfPm3Iso14443a, "Perma lock UID changes. irreversible"}, // {"-----------", CmdHelp, IfPm3Iso14443a, "----------------------- " _CYAN_("i") " -----------------------"}, // {"ice", CmdHF14AMfice, IfPm3Iso14443a, "collect MIFARE Classic nonces to file"}, {NULL, NULL, NULL, NULL} }; static int CmdHelp(const char *Cmd) { (void)Cmd; // Cmd is not used so far CmdsHelp(CommandTable); return PM3_SUCCESS; } int CmdHFMF(const char *Cmd) { clearCommandBuffer(); return CmdsParse(CommandTable, Cmd); }