X-Git-Url: http://cvs.zerfleddert.de/cgi-bin/gitweb.cgi/proxmark3-svn/blobdiff_plain/4cb4b588c276f8d3c3b4a18dc292d3bfd0e497b9..1d04b933df776bf60489b67bb5c9b806e308dfc8:/client/cmdhfmf.c diff --git a/client/cmdhfmf.c b/client/cmdhfmf.c index 7a6aaa3b..93c3ea65 100644 --- a/client/cmdhfmf.c +++ b/client/cmdhfmf.c @@ -8,41 +8,53 @@ // High frequency MIFARE commands //----------------------------------------------------------------------------- +#include "cmdhfmf.h" + #include #include #include #include #include -#include "proxmark3.h" +#include "comms.h" #include "cmdmain.h" +#include "cmdhfmfhard.h" +#include "parity.h" #include "util.h" +#include "util_posix.h" +#include "usb_cmd.h" #include "ui.h" -#include "mifarehost.h" +#include "mifare/mifarehost.h" #include "mifare.h" -#include "mfkey.h" - -#define NESTED_SECTOR_RETRY 10 // how often we try mfested() until we give up - +#include "mifare/mfkey.h" +#include "hardnested/hardnested_bf_core.h" +#include "cliparser/cliparser.h" +#include "cmdhf14a.h" +#include "mifare/mifaredefault.h" +#include "mifare/mifare4.h" +#include "mifare/mad.h" +#include "mifare/ndef.h" +#include "emv/dump.h" +#include "protocols.h" + +#define NESTED_SECTOR_RETRY 10 // how often we try mfested() until we give up static int CmdHelp(const char *Cmd); - int CmdHF14AMifare(const char *Cmd) { int isOK = 0; uint64_t key = 0; - isOK = mfDarkside(&key); switch (isOK) { case -1 : PrintAndLog("Button pressed. Aborted."); return 1; case -2 : PrintAndLog("Card is not vulnerable to Darkside attack (doesn't send NACK on authentication requests)."); return 1; case -3 : PrintAndLog("Card is not vulnerable to Darkside attack (its random number generator is not predictable)."); return 1; case -4 : PrintAndLog("Card is not vulnerable to Darkside attack (its random number generator seems to be based on the wellknown"); - PrintAndLog("generating polynomial with 16 effective bits only, but shows unexpected behaviour."); return 1; + PrintAndLog("generating polynomial with 16 effective bits only, but shows unexpected behaviour."); return 1; case -5 : PrintAndLog("Aborted via keyboard."); return 1; default : PrintAndLog("Found valid key:%012" PRIx64 "\n", key); } - + PrintAndLog(""); return 0; } @@ -54,14 +66,14 @@ int CmdHF14AMfWrBl(const char *Cmd) uint8_t keyType = 0; uint8_t key[6] = {0, 0, 0, 0, 0, 0}; uint8_t bldata[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - - char cmdp = 0x00; + + char cmdp = 0x00; if (strlen(Cmd)<3) { PrintAndLog("Usage: hf mf wrbl "); PrintAndLog(" sample: hf mf wrbl 0 A FFFFFFFFFFFF 000102030405060708090A0B0C0D0E0F"); return 0; - } + } blockNo = param_get8(Cmd, 0); cmdp = param_getchar(Cmd, 1); @@ -80,11 +92,11 @@ int CmdHF14AMfWrBl(const char *Cmd) } PrintAndLog("--block no:%d, key type:%c, key:%s", blockNo, keyType?'B':'A', sprint_hex(key, 6)); PrintAndLog("--data: %s", sprint_hex(bldata, 16)); - - UsbCommand c = {CMD_MIFARE_WRITEBL, {blockNo, keyType, 0}}; + + UsbCommand c = {CMD_MIFARE_WRITEBL, {blockNo, keyType, 0}}; memcpy(c.d.asBytes, key, 6); memcpy(c.d.asBytes + 10, bldata, 16); - SendCommand(&c); + SendCommand(&c); UsbCommand resp; if (WaitForResponseTimeout(CMD_ACK,&resp,1500)) { @@ -102,16 +114,16 @@ int CmdHF14AMfRdBl(const char *Cmd) uint8_t blockNo = 0; uint8_t keyType = 0; uint8_t key[6] = {0, 0, 0, 0, 0, 0}; - - char cmdp = 0x00; + + char cmdp = 0x00; if (strlen(Cmd)<3) { PrintAndLog("Usage: hf mf rdbl "); PrintAndLog(" sample: hf mf rdbl 0 A FFFFFFFFFFFF "); return 0; - } - + } + blockNo = param_get8(Cmd, 0); cmdp = param_getchar(Cmd, 1); if (cmdp == 0x00) { @@ -124,25 +136,39 @@ int CmdHF14AMfRdBl(const char *Cmd) return 1; } PrintAndLog("--block no:%d, key type:%c, key:%s ", blockNo, keyType?'B':'A', sprint_hex(key, 6)); - - UsbCommand c = {CMD_MIFARE_READBL, {blockNo, keyType, 0}}; + + UsbCommand c = {CMD_MIFARE_READBL, {blockNo, keyType, 0}}; memcpy(c.d.asBytes, key, 6); - SendCommand(&c); + SendCommand(&c); UsbCommand resp; if (WaitForResponseTimeout(CMD_ACK,&resp,1500)) { uint8_t isOK = resp.arg[0] & 0xff; uint8_t *data = resp.d.asBytes; - if (isOK) + if (isOK) { PrintAndLog("isOk:%02x data:%s", isOK, sprint_hex(data, 16)); - else + } else { PrintAndLog("isOk:%02x", isOK); + return 1; + } + + if (mfIsSectorTrailer(blockNo) && (data[6] || data[7] || data[8])) { + PrintAndLogEx(NORMAL, "Trailer decoded:"); + int bln = mfFirstBlockOfSector(mfSectorNum(blockNo)); + int blinc = (mfNumBlocksPerSector(mfSectorNum(blockNo)) > 4) ? 5 : 1; + for (int i = 0; i < 4; i++) { + PrintAndLogEx(NORMAL, "Access block %d%s: %s", bln, ((blinc > 1) && (i < 3) ? "+" : "") , mfGetAccessConditionsDesc(i, &data[6])); + bln += blinc; + } + PrintAndLogEx(NORMAL, "UserData: %s", sprint_hex_inrow(&data[9], 1)); + } } else { PrintAndLog("Command execute timeout"); + return 2; } - return 0; + return 0; } int CmdHF14AMfRdSc(const char *Cmd) @@ -153,14 +179,14 @@ int CmdHF14AMfRdSc(const char *Cmd) uint8_t key[6] = {0, 0, 0, 0, 0, 0}; uint8_t isOK = 0; uint8_t *data = NULL; - char cmdp = 0x00; + char cmdp = 0x00; if (strlen(Cmd)<3) { PrintAndLog("Usage: hf mf rdsc "); PrintAndLog(" sample: hf mf rdsc 0 A FFFFFFFFFFFF "); return 0; - } - + } + sectorNo = param_get8(Cmd, 0); if (sectorNo > 39) { PrintAndLog("Sector number must be less than 40"); @@ -177,7 +203,7 @@ int CmdHF14AMfRdSc(const char *Cmd) return 1; } PrintAndLog("--sector no:%d key type:%c key:%s ", sectorNo, keyType?'B':'A', sprint_hex(key, 6)); - + UsbCommand c = {CMD_MIFARE_READSC, {sectorNo, keyType, 0}}; memcpy(c.d.asBytes, key, 6); SendCommand(&c); @@ -194,12 +220,21 @@ int CmdHF14AMfRdSc(const char *Cmd) PrintAndLog("data : %s", sprint_hex(data + i * 16, 16)); } PrintAndLog("trailer: %s", sprint_hex(data + (sectorNo<32?3:15) * 16, 16)); + + PrintAndLogEx(NORMAL, "Trailer decoded:"); + int bln = mfFirstBlockOfSector(sectorNo); + int blinc = (mfNumBlocksPerSector(sectorNo) > 4) ? 5 : 1; + for (i = 0; i < 4; i++) { + PrintAndLogEx(NORMAL, "Access block %d%s: %s", bln, ((blinc > 1) && (i < 3) ? "+" : "") , mfGetAccessConditionsDesc(i, &(data + (sectorNo<32?3:15) * 16)[6])); + bln += blinc; + } + PrintAndLogEx(NORMAL, "UserData: %s", sprint_hex_inrow(&(data + (sectorNo<32?3:15) * 16)[9], 1)); } } else { PrintAndLog("Command execute timeout"); } - return 0; + return 0; } uint8_t FirstBlockOfSector(uint8_t sectorNo) @@ -220,65 +255,80 @@ uint8_t NumBlocksPerSector(uint8_t sectorNo) } } +static int ParamCardSizeSectors(const char c) { + int numSectors = 16; + switch (c) { + case '0' : numSectors = 5; break; + case '2' : numSectors = 32; break; + case '4' : numSectors = 40; break; + default: numSectors = 16; + } + return numSectors; +} + +static int ParamCardSizeBlocks(const char c) { + int numBlocks = 16 * 4; + switch (c) { + case '0' : numBlocks = 5 * 4; break; + case '2' : numBlocks = 32 * 4; break; + case '4' : numBlocks = 32 * 4 + 8 * 16; break; + default: numBlocks = 16 * 4; + } + return numBlocks; +} + int CmdHF14AMfDump(const char *Cmd) { uint8_t sectorNo, blockNo; - - uint8_t keyA[40][6]; - uint8_t keyB[40][6]; + + uint8_t keys[2][40][6]; uint8_t rights[40][4]; uint8_t carddata[256][16]; uint8_t numSectors = 16; - + FILE *fin; FILE *fout; - + UsbCommand resp; char cmdp = param_getchar(Cmd, 0); - switch (cmdp) { - case '0' : numSectors = 5; break; - case '1' : - case '\0': numSectors = 16; break; - case '2' : numSectors = 32; break; - case '4' : numSectors = 40; break; - default: numSectors = 16; - } - - if (strlen(Cmd) > 1 || cmdp == 'h' || cmdp == 'H') { - PrintAndLog("Usage: hf mf dump [card memory]"); + numSectors = ParamCardSizeSectors(cmdp); + + if (strlen(Cmd) > 3 || cmdp == 'h' || cmdp == 'H') { + PrintAndLog("Usage: hf mf dump [card memory] [k|m]"); PrintAndLog(" [card memory]: 0 = 320 bytes (Mifare Mini), 1 = 1K (default), 2 = 2K, 4 = 4K"); + PrintAndLog(" k: Always try using both Key A and Key B for each sector, even if access bits would prohibit it"); + PrintAndLog(" m: When missing access bits or keys, replace that block with NULL"); PrintAndLog(""); PrintAndLog("Samples: hf mf dump"); PrintAndLog(" hf mf dump 4"); + PrintAndLog(" hf mf dump 4 m"); return 0; } - + + char opts = param_getchar(Cmd, 1); + bool useBothKeysAlways = false; + if (opts == 'k' || opts == 'K') useBothKeysAlways = true; + bool nullMissingKeys = false; + if (opts == 'm' || opts == 'M') nullMissingKeys = true; + if ((fin = fopen("dumpkeys.bin","rb")) == NULL) { PrintAndLog("Could not find file dumpkeys.bin"); return 1; } - - // Read keys A from file - for (sectorNo=0; sectorNo 1 || cmdp == 'h' || cmdp == 'H') { PrintAndLog("Usage: hf mf restore [card memory]"); @@ -434,7 +493,7 @@ int CmdHF14AMfRestore(const char *Cmd) PrintAndLog("Could not find file dumpkeys.bin"); return 1; } - + for (sectorNo = 0; sectorNo < numSectors; sectorNo++) { size_t bytes_read = fread(keyA[sectorNo], 1, 6, fkeys); if (bytes_read != 6) { @@ -458,22 +517,22 @@ int CmdHF14AMfRestore(const char *Cmd) if ((fdump = fopen("dumpdata.bin","rb")) == NULL) { PrintAndLog("Could not find file dumpdata.bin"); return 1; - } + } PrintAndLog("Restoring dumpdata.bin to card"); for (sectorNo = 0; sectorNo < numSectors; sectorNo++) { for(blockNo = 0; blockNo < NumBlocksPerSector(sectorNo); blockNo++) { UsbCommand c = {CMD_MIFARE_WRITEBL, {FirstBlockOfSector(sectorNo) + blockNo, keyType, 0}}; memcpy(c.d.asBytes, key, 6); - + size_t bytes_read = fread(bldata, 1, 16, fdump); if (bytes_read != 16) { PrintAndLog("File reading error (dumpdata.bin)."); fclose(fdump); return 2; } - - if (blockNo == NumBlocksPerSector(sectorNo) - 1) { // sector trailer + + if (blockNo == NumBlocksPerSector(sectorNo) - 1) { // sector trailer bldata[0] = (keyA[sectorNo][0]); bldata[1] = (keyA[sectorNo][1]); bldata[2] = (keyA[sectorNo][2]); @@ -486,10 +545,10 @@ int CmdHF14AMfRestore(const char *Cmd) bldata[13] = (keyB[sectorNo][3]); bldata[14] = (keyB[sectorNo][4]); bldata[15] = (keyB[sectorNo][5]); - } - + } + PrintAndLog("Writing to block %3d: %s", FirstBlockOfSector(sectorNo) + blockNo, sprint_hex(bldata, 16)); - + memcpy(c.d.asBytes + 10, bldata, 16); SendCommand(&c); @@ -502,20 +561,40 @@ int CmdHF14AMfRestore(const char *Cmd) } } } - + fclose(fdump); return 0; } +//---------------------------------------------- +// Nested +//---------------------------------------------- -typedef struct { - uint64_t Key[2]; - int foundKey[2]; -} sector_t; +static void parseParamTDS(const char *Cmd, const uint8_t indx, bool *paramT, bool *paramD, uint16_t *timeout) { + char ctmp3[4] = {0}; + int len = param_getlength(Cmd, indx); + if (len > 0 && len < 4){ + param_getstr(Cmd, indx, ctmp3, sizeof(ctmp3)); + *paramT |= (ctmp3[0] == 't' || ctmp3[0] == 'T'); + *paramD |= (ctmp3[0] == 'd' || ctmp3[0] == 'D'); + bool paramS1 = *paramT || *paramD; -int CmdHF14AMfNested(const char *Cmd) -{ + // slow and very slow + if (ctmp3[0] == 's' || ctmp3[0] == 'S' || ctmp3[1] == 's' || ctmp3[1] == 'S') { + *timeout = MF_CHKKEYS_SLOWTIMEOUT; // slow + + if (!paramS1 && (ctmp3[1] == 's' || ctmp3[1] == 'S')) { + *timeout = MF_CHKKEYS_VERYSLOWTIMEOUT; // very slow + } + if (paramS1 && (ctmp3[2] == 's' || ctmp3[2] == 'S')) { + *timeout = MF_CHKKEYS_VERYSLOWTIMEOUT; // very slow + } + } + } +} + +int CmdHF14AMfNested(const char *Cmd) { int i, j, res, iterations; sector_t *e_sector = NULL; uint8_t blockNo = 0; @@ -524,110 +603,138 @@ int CmdHF14AMfNested(const char *Cmd) uint8_t trgKeyType = 0; uint8_t SectorsCnt = 0; uint8_t key[6] = {0, 0, 0, 0, 0, 0}; - uint8_t keyBlock[14*6]; + uint8_t keyBlock[MifareDefaultKeysSize * 6]; uint64_t key64 = 0; + // timeout in units. (ms * 106) or us*0.106 + uint16_t timeout14a = MF_CHKKEYS_DEFTIMEOUT; // fast by default + + bool autosearchKey = false; + bool transferToEml = false; - bool createDumpFile = false; FILE *fkeys; uint8_t standart[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; uint8_t tempkey[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; - + char cmdp, ctmp; if (strlen(Cmd)<3) { PrintAndLog("Usage:"); - PrintAndLog(" all sectors: hf mf nested [t,d]"); + PrintAndLog(" all sectors: hf mf nested [t|d|s|ss]"); + PrintAndLog(" all sectors autosearch key: hf mf nested * [t|d|s|ss]"); PrintAndLog(" one sector: hf mf nested o "); PrintAndLog(" [t]"); + PrintAndLog(" "); PrintAndLog("card memory - 0 - MINI(320 bytes), 1 - 1K, 2 - 2K, 4 - 4K, - 1K"); - PrintAndLog("t - transfer keys into emulator memory"); - PrintAndLog("d - write keys to binary file"); + PrintAndLog("t - transfer keys to emulator memory"); + PrintAndLog("d - write keys to binary file dumpkeys.bin"); + PrintAndLog("s - Slow (1ms) check keys (required by some non standard cards)"); + PrintAndLog("ss - Very slow (5ms) check keys"); PrintAndLog(" "); PrintAndLog(" sample1: hf mf nested 1 0 A FFFFFFFFFFFF "); PrintAndLog(" sample2: hf mf nested 1 0 A FFFFFFFFFFFF t "); PrintAndLog(" sample3: hf mf nested 1 0 A FFFFFFFFFFFF d "); PrintAndLog(" sample4: hf mf nested o 0 A FFFFFFFFFFFF 4 A"); + PrintAndLog(" sample5: hf mf nested 1 * t"); + PrintAndLog(" sample6: hf mf nested 1 * ss"); return 0; - } - - cmdp = param_getchar(Cmd, 0); - blockNo = param_get8(Cmd, 1); - ctmp = param_getchar(Cmd, 2); - - if (ctmp != 'a' && ctmp != 'A' && ctmp != 'b' && ctmp != 'B') { - PrintAndLog("Key type must be A or B"); - return 1; - } - - if (ctmp != 'A' && ctmp != 'a') - keyType = 1; - - if (param_gethex(Cmd, 3, key, 12)) { - PrintAndLog("Key must include 12 HEX symbols"); - return 1; } - + + // + cmdp = param_getchar(Cmd, 0); if (cmdp == 'o' || cmdp == 'O') { cmdp = 'o'; - trgBlockNo = param_get8(Cmd, 4); - ctmp = param_getchar(Cmd, 5); + SectorsCnt = 1; + } else { + SectorsCnt = ParamCardSizeSectors(cmdp); + } + + // . number or autosearch key (*) + if (param_getchar(Cmd, 1) == '*') { + autosearchKey = true; + + parseParamTDS(Cmd, 2, &transferToEml, &createDumpFile, &timeout14a); + + PrintAndLog("--nested. sectors:%2d, block no:*, eml:%c, dmp=%c checktimeout=%d us", + SectorsCnt, transferToEml?'y':'n', createDumpFile?'y':'n', ((uint32_t)timeout14a * 1000) / 106); + } else { + blockNo = param_get8(Cmd, 1); + + ctmp = param_getchar(Cmd, 2); if (ctmp != 'a' && ctmp != 'A' && ctmp != 'b' && ctmp != 'B') { - PrintAndLog("Target key type must be A or B"); + PrintAndLog("Key type must be A or B"); return 1; } - if (ctmp != 'A' && ctmp != 'a') - trgKeyType = 1; - } else { - - switch (cmdp) { - case '0': SectorsCnt = 05; break; - case '1': SectorsCnt = 16; break; - case '2': SectorsCnt = 32; break; - case '4': SectorsCnt = 40; break; - default: SectorsCnt = 16; - } - } - - ctmp = param_getchar(Cmd, 4); - if (ctmp == 't' || ctmp == 'T') transferToEml = true; - else if (ctmp == 'd' || ctmp == 'D') createDumpFile = true; - - ctmp = param_getchar(Cmd, 6); - transferToEml |= (ctmp == 't' || ctmp == 'T'); - transferToEml |= (ctmp == 'd' || ctmp == 'D'); - - if (cmdp == 'o') { + + if (ctmp != 'A' && ctmp != 'a') + keyType = 1; + + if (param_gethex(Cmd, 3, key, 12)) { + PrintAndLog("Key must include 12 HEX symbols"); + return 1; + } + + // check if we can authenticate to sector + res = mfCheckKeys(blockNo, keyType, timeout14a, true, 1, key, &key64); + if (res) { + PrintAndLog("Can't authenticate to block:%3d key type:%c key:%s", blockNo, keyType?'B':'A', sprint_hex(key, 6)); + return 3; + } + + // one sector nested + if (cmdp == 'o') { + trgBlockNo = param_get8(Cmd, 4); + + ctmp = param_getchar(Cmd, 5); + if (ctmp != 'a' && ctmp != 'A' && ctmp != 'b' && ctmp != 'B') { + PrintAndLog("Target key type must be A or B"); + return 1; + } + if (ctmp != 'A' && ctmp != 'a') + trgKeyType = 1; + + parseParamTDS(Cmd, 6, &transferToEml, &createDumpFile, &timeout14a); + } else { + parseParamTDS(Cmd, 4, &transferToEml, &createDumpFile, &timeout14a); + } + + PrintAndLog("--nested. sectors:%2d, block no:%3d, key type:%c, eml:%c, dmp=%c checktimeout=%d us", + SectorsCnt, blockNo, keyType?'B':'A', transferToEml?'y':'n', createDumpFile?'y':'n', ((uint32_t)timeout14a * 1000) / 106); + } + + // one-sector nested + if (cmdp == 'o') { // ------------------------------------ one sector working PrintAndLog("--target block no:%3d, target key type:%c ", trgBlockNo, trgKeyType?'B':'A'); - int16_t isOK = mfnested(blockNo, keyType, key, trgBlockNo, trgKeyType, keyBlock, true); - if (isOK) { + int16_t isOK = mfnested(blockNo, keyType, timeout14a, key, trgBlockNo, trgKeyType, keyBlock, true); + if (isOK < 0) { switch (isOK) { case -1 : PrintAndLog("Error: No response from Proxmark.\n"); break; case -2 : PrintAndLog("Button pressed. Aborted.\n"); break; case -3 : PrintAndLog("Tag isn't vulnerable to Nested Attack (random numbers are not predictable).\n"); break; - default : PrintAndLog("Unknown Error.\n"); + default : PrintAndLog("Unknown Error (%d)\n", isOK); } return 2; } key64 = bytes_to_num(keyBlock, 6); - if (key64) { + if (!isOK) { PrintAndLog("Found valid key:%012" PRIx64, key64); // transfer key to the emulator if (transferToEml) { uint8_t sectortrailer; - if (trgBlockNo < 32*4) { // 4 block sector - sectortrailer = (trgBlockNo & 0x03) + 3; - } else { // 16 block sector - sectortrailer = (trgBlockNo & 0x0f) + 15; + if (trgBlockNo < 32*4) { // 4 block sector + sectortrailer = trgBlockNo | 0x03; + } else { // 16 block sector + sectortrailer = trgBlockNo | 0x0f; } mfEmlGetMem(keyBlock, sectortrailer, 1); - + if (!trgKeyType) num_to_bytes(key64, 6, keyBlock); else num_to_bytes(key64, 6, &keyBlock[10]); - mfEmlSetMem(keyBlock, sectortrailer, 1); + mfEmlSetMem(keyBlock, sectortrailer, 1); + PrintAndLog("Key transferred to emulator memory."); } } else { PrintAndLog("No valid key found"); @@ -639,76 +746,84 @@ int CmdHF14AMfNested(const char *Cmd) e_sector = calloc(SectorsCnt, sizeof(sector_t)); if (e_sector == NULL) return 1; - + //test current key and additional standard keys first - memcpy(keyBlock, key, 6); - num_to_bytes(0xffffffffffff, 6, (uint8_t*)(keyBlock + 1 * 6)); - num_to_bytes(0x000000000000, 6, (uint8_t*)(keyBlock + 2 * 6)); - num_to_bytes(0xa0a1a2a3a4a5, 6, (uint8_t*)(keyBlock + 3 * 6)); - num_to_bytes(0xb0b1b2b3b4b5, 6, (uint8_t*)(keyBlock + 4 * 6)); - num_to_bytes(0xaabbccddeeff, 6, (uint8_t*)(keyBlock + 5 * 6)); - num_to_bytes(0x4d3a99c351dd, 6, (uint8_t*)(keyBlock + 6 * 6)); - num_to_bytes(0x1a982c7e459a, 6, (uint8_t*)(keyBlock + 7 * 6)); - num_to_bytes(0xd3f7d3f7d3f7, 6, (uint8_t*)(keyBlock + 8 * 6)); - num_to_bytes(0x714c5c886e97, 6, (uint8_t*)(keyBlock + 9 * 6)); - num_to_bytes(0x587ee5f9350f, 6, (uint8_t*)(keyBlock + 10 * 6)); - num_to_bytes(0xa0478cc39091, 6, (uint8_t*)(keyBlock + 11 * 6)); - num_to_bytes(0x533cb6c723f6, 6, (uint8_t*)(keyBlock + 12 * 6)); - num_to_bytes(0x8fd0a4f256e9, 6, (uint8_t*)(keyBlock + 13 * 6)); + for (int defaultKeyCounter = 0; defaultKeyCounter < MifareDefaultKeysSize; defaultKeyCounter++){ + num_to_bytes(MifareDefaultKeys[defaultKeyCounter], 6, (uint8_t*)(keyBlock + defaultKeyCounter * 6)); + } PrintAndLog("Testing known keys. Sector count=%d", SectorsCnt); - for (i = 0; i < SectorsCnt; i++) { - for (j = 0; j < 2; j++) { - if (e_sector[i].foundKey[j]) continue; - - res = mfCheckKeys(FirstBlockOfSector(i), j, true, 6, keyBlock, &key64); - - if (!res) { - e_sector[i].Key[j] = key64; - e_sector[i].foundKey[j] = 1; + mfCheckKeysSec(SectorsCnt, 2, timeout14a, true, true, true, MifareDefaultKeysSize, keyBlock, e_sector); + + // get known key from array + bool keyFound = false; + if (autosearchKey) { + for (i = 0; i < SectorsCnt; i++) { + for (j = 0; j < 2; j++) { + if (e_sector[i].foundKey[j]) { + // get known key + blockNo = i * 4; + keyType = j; + num_to_bytes(e_sector[i].Key[j], 6, key); + keyFound = true; + break; + } } + if (keyFound) break; + } + + // Can't found a key.... + if (!keyFound) { + PrintAndLog("Can't found any of the known keys."); + free(e_sector); + return 4; } + PrintAndLog("--auto key. block no:%3d, key type:%c key:%s", blockNo, keyType?'B':'A', sprint_hex(key, 6)); } - + // nested sectors iterations = 0; PrintAndLog("nested..."); bool calibrate = true; for (i = 0; i < NESTED_SECTOR_RETRY; i++) { for (uint8_t sectorNo = 0; sectorNo < SectorsCnt; sectorNo++) { - for (trgKeyType = 0; trgKeyType < 2; trgKeyType++) { + for (trgKeyType = 0; trgKeyType < 2; trgKeyType++) { if (e_sector[sectorNo].foundKey[trgKeyType]) continue; PrintAndLog("-----------------------------------------------"); - int16_t isOK = mfnested(blockNo, keyType, key, FirstBlockOfSector(sectorNo), trgKeyType, keyBlock, calibrate); - if(isOK) { + int16_t isOK = mfnested(blockNo, keyType, timeout14a, key, FirstBlockOfSector(sectorNo), trgKeyType, keyBlock, calibrate); + if(isOK < 0) { switch (isOK) { case -1 : PrintAndLog("Error: No response from Proxmark.\n"); break; case -2 : PrintAndLog("Button pressed. Aborted.\n"); break; case -3 : PrintAndLog("Tag isn't vulnerable to Nested Attack (random numbers are not predictable).\n"); break; - default : PrintAndLog("Unknown Error.\n"); + default : PrintAndLog("Unknown Error (%d)\n", isOK); } free(e_sector); return 2; } else { calibrate = false; } - + iterations++; key64 = bytes_to_num(keyBlock, 6); - if (key64) { + if (!isOK) { PrintAndLog("Found valid key:%012" PRIx64, key64); e_sector[sectorNo].foundKey[trgKeyType] = 1; e_sector[sectorNo].Key[trgKeyType] = key64; + + // try to check this key as a key to the other sectors + mfCheckKeysSec(SectorsCnt, 2, timeout14a, true, true, true, 1, keyBlock, e_sector); } } } } - printf("Time in nested: %1.3f (%1.3f sec per key)\n\n", ((float)(msclock() - msclock1))/1000.0, ((float)(msclock() - msclock1))/iterations/1000.0); - - PrintAndLog("-----------------------------------------------\nIterations count: %d\n\n", iterations); - //print them + // print nested statistic + PrintAndLog("\n\n-----------------------------------------------\nNested statistic:\nIterations count: %d", iterations); + PrintAndLog("Time in nested: %1.3f (%1.3f sec per key)", ((float)(msclock() - msclock1))/1000.0, ((float)(msclock() - msclock1))/iterations/1000.0); + + // print result PrintAndLog("|---|----------------|---|----------------|---|"); PrintAndLog("|sec|key A |res|key B |res|"); PrintAndLog("|---|----------------|---|----------------|---|"); @@ -717,8 +832,8 @@ int CmdHF14AMfNested(const char *Cmd) e_sector[i].Key[0], e_sector[i].foundKey[0], e_sector[i].Key[1], e_sector[i].foundKey[1]); } PrintAndLog("|---|----------------|---|----------------|---|"); - - // transfer them to the emulator + + // transfer keys to the emulator memory if (transferToEml) { for (i = 0; i < SectorsCnt; i++) { mfEmlGetMem(keyBlock, FirstBlockOfSector(i) + NumBlocksPerSector(i) - 1, 1); @@ -727,12 +842,13 @@ int CmdHF14AMfNested(const char *Cmd) if (e_sector[i].foundKey[1]) num_to_bytes(e_sector[i].Key[1], 6, &keyBlock[10]); mfEmlSetMem(keyBlock, FirstBlockOfSector(i) + NumBlocksPerSector(i) - 1, 1); - } + } + PrintAndLog("Keys transferred to emulator memory."); } - + // Create dump file if (createDumpFile) { - if ((fkeys = fopen("dumpkeys.bin","wb")) == NULL) { + if ((fkeys = fopen("dumpkeys.bin","wb")) == NULL) { PrintAndLog("Could not create file dumpkeys.bin"); free(e_sector); return 1; @@ -758,103 +874,279 @@ int CmdHF14AMfNested(const char *Cmd) } fclose(fkeys); } - + free(e_sector); } return 0; } -int CmdHF14AMfChk(const char *Cmd) + +int CmdHF14AMfNestedHard(const char *Cmd) { + uint8_t blockNo = 0; + uint8_t keyType = 0; + uint8_t trgBlockNo = 0; + uint8_t trgKeyType = 0; + uint8_t key[6] = {0, 0, 0, 0, 0, 0}; + uint8_t trgkey[6] = {0, 0, 0, 0, 0, 0}; + + char ctmp; + ctmp = param_getchar(Cmd, 0); + + if (ctmp != 'R' && ctmp != 'r' && ctmp != 'T' && ctmp != 't' && strlen(Cmd) < 20) { + PrintAndLog("Usage:"); + PrintAndLog(" hf mf hardnested "); + PrintAndLog(" [known target key (12 hex symbols)] [w] [s]"); + PrintAndLog(" or hf mf hardnested r [known target key]"); + PrintAndLog(" "); + PrintAndLog("Options: "); + PrintAndLog(" w: Acquire nonces and write them to binary file nonces.bin"); + PrintAndLog(" s: Slower acquisition (required by some non standard cards)"); + PrintAndLog(" r: Read nonces.bin and start attack"); + PrintAndLog(" iX: set type of SIMD instructions. Without this flag programs autodetect it."); + PrintAndLog(" i5: AVX512"); + PrintAndLog(" i2: AVX2"); + PrintAndLog(" ia: AVX"); + PrintAndLog(" is: SSE2"); + PrintAndLog(" im: MMX"); + PrintAndLog(" in: none (use CPU regular instruction set)"); + PrintAndLog(" "); + PrintAndLog(" sample1: hf mf hardnested 0 A FFFFFFFFFFFF 4 A"); + PrintAndLog(" sample2: hf mf hardnested 0 A FFFFFFFFFFFF 4 A w"); + PrintAndLog(" sample3: hf mf hardnested 0 A FFFFFFFFFFFF 4 A w s"); + PrintAndLog(" sample4: hf mf hardnested r"); + PrintAndLog(" "); + PrintAndLog("Add the known target key to check if it is present in the remaining key space:"); + PrintAndLog(" sample5: hf mf hardnested 0 A A0A1A2A3A4A5 4 A FFFFFFFFFFFF"); + return 0; + } + + bool know_target_key = false; + bool nonce_file_read = false; + bool nonce_file_write = false; + bool slow = false; + int tests = 0; + + + uint16_t iindx = 0; + if (ctmp == 'R' || ctmp == 'r') { + nonce_file_read = true; + iindx = 1; + if (!param_gethex(Cmd, 1, trgkey, 12)) { + know_target_key = true; + iindx = 2; + } + } else if (ctmp == 'T' || ctmp == 't') { + tests = param_get32ex(Cmd, 1, 100, 10); + iindx = 2; + if (!param_gethex(Cmd, 2, trgkey, 12)) { + know_target_key = true; + iindx = 3; + } + } else { + blockNo = param_get8(Cmd, 0); + ctmp = param_getchar(Cmd, 1); + if (ctmp != 'a' && ctmp != 'A' && ctmp != 'b' && ctmp != 'B') { + PrintAndLog("Key type must be A or B"); + return 1; + } + if (ctmp != 'A' && ctmp != 'a') { + keyType = 1; + } + + if (param_gethex(Cmd, 2, key, 12)) { + PrintAndLog("Key must include 12 HEX symbols"); + return 1; + } + + trgBlockNo = param_get8(Cmd, 3); + ctmp = param_getchar(Cmd, 4); + if (ctmp != 'a' && ctmp != 'A' && ctmp != 'b' && ctmp != 'B') { + PrintAndLog("Target key type must be A or B"); + return 1; + } + if (ctmp != 'A' && ctmp != 'a') { + trgKeyType = 1; + } + + uint16_t i = 5; + + if (!param_gethex(Cmd, 5, trgkey, 12)) { + know_target_key = true; + i++; + } + iindx = i; + + while ((ctmp = param_getchar(Cmd, i))) { + if (ctmp == 's' || ctmp == 'S') { + slow = true; + } else if (ctmp == 'w' || ctmp == 'W') { + nonce_file_write = true; + } else if (param_getlength(Cmd, i) == 2 && ctmp == 'i') { + iindx = i; + } else { + PrintAndLog("Possible options are w , s and/or iX"); + return 1; + } + i++; + } + } + + SetSIMDInstr(SIMD_AUTO); + if (iindx > 0) { + while ((ctmp = param_getchar(Cmd, iindx))) { + if (param_getlength(Cmd, iindx) == 2 && ctmp == 'i') { + switch(param_getchar_indx(Cmd, 1, iindx)) { + case '5': + SetSIMDInstr(SIMD_AVX512); + break; + case '2': + SetSIMDInstr(SIMD_AVX2); + break; + case 'a': + SetSIMDInstr(SIMD_AVX); + break; + case 's': + SetSIMDInstr(SIMD_SSE2); + break; + case 'm': + SetSIMDInstr(SIMD_MMX); + break; + case 'n': + SetSIMDInstr(SIMD_NONE); + break; + default: + PrintAndLog("Unknown SIMD type. %c", param_getchar_indx(Cmd, 1, iindx)); + return 1; + } + } + iindx++; + } + } + + PrintAndLog("--target block no:%3d, target key type:%c, known target key: 0x%02x%02x%02x%02x%02x%02x%s, file action: %s, Slow: %s, Tests: %d ", + trgBlockNo, + trgKeyType?'B':'A', + trgkey[0], trgkey[1], trgkey[2], trgkey[3], trgkey[4], trgkey[5], + know_target_key?"":" (not set)", + nonce_file_write?"write":nonce_file_read?"read":"none", + slow?"Yes":"No", + tests); + + int16_t isOK = mfnestedhard(blockNo, keyType, key, trgBlockNo, trgKeyType, know_target_key?trgkey:NULL, nonce_file_read, nonce_file_write, slow, tests); + + if (isOK) { + switch (isOK) { + case 1 : PrintAndLog("Error: No response from Proxmark.\n"); break; + case 2 : PrintAndLog("Button pressed. Aborted.\n"); break; + default : break; + } + return 2; + } + + return 0; +} + + +int CmdHF14AMfChk(const char *Cmd) { + if (strlen(Cmd)<3) { - PrintAndLog("Usage: hf mf chk |<*card memory> [t|d] [] []"); + PrintAndLog("Usage: hf mf chk |<*card memory> [t|d|s|ss] [] []"); PrintAndLog(" * - all sectors"); PrintAndLog("card memory - 0 - MINI(320 bytes), 1 - 1K, 2 - 2K, 4 - 4K, - 1K"); - PrintAndLog("d - write keys to binary file\n"); - PrintAndLog("t - write keys to emulator memory"); + PrintAndLog("d - write keys to binary file (not used when supplied)"); + PrintAndLog("t - write keys to emulator memory"); + PrintAndLog("s - slow execute. timeout 1ms"); + PrintAndLog("ss - very slow execute. timeout 5ms"); PrintAndLog(" sample: hf mf chk 0 A 1234567890ab keys.dic"); PrintAndLog(" hf mf chk *1 ? t"); PrintAndLog(" hf mf chk *1 ? d"); + PrintAndLog(" hf mf chk *1 ? s"); + PrintAndLog(" hf mf chk *1 ? dss"); return 0; - } + } - FILE * f; - char filename[FILE_PATH_SIZE]={0}; - char buf[13]; - uint8_t *keyBlock = NULL, *p; - uint8_t stKeyBlock = 20; - - int i, res; - int keycnt = 0; - char ctmp = 0x00; - uint8_t blockNo = 0; - uint8_t SectorsCnt = 1; - uint8_t keyType = 0; - uint64_t key64 = 0; - - int transferToEml = 0; - int createDumpFile = 0; + FILE * f; + char filename[FILE_PATH_SIZE]={0}; + char buf[13]; + uint8_t *keyBlock = NULL, *p; + uint16_t stKeyBlock = 20; + int i, res; + int keycnt = 0; + char ctmp = 0x00; + int clen = 0; + uint8_t blockNo = 0; + uint8_t SectorsCnt = 0; + uint8_t keyType = 0; + uint64_t key64 = 0; + // timeout in units. (ms * 106)/10 or us*0.0106 + uint16_t timeout14a = MF_CHKKEYS_DEFTIMEOUT; // fast by default + bool param3InUse = false; + bool transferToEml = 0; + bool createDumpFile = 0; + bool singleBlock = false; // Flag to ID if a single or multi key check + uint8_t keyFoundCount = 0; // Counter to display the number of keys found/transfered to emulator + + sector_t *e_sector = NULL; keyBlock = calloc(stKeyBlock, 6); if (keyBlock == NULL) return 1; - uint64_t defaultKeys[] = - { - 0xffffffffffff, // Default key (first key used by program if no user defined key) - 0x000000000000, // Blank key - 0xa0a1a2a3a4a5, // NFCForum MAD key - 0xb0b1b2b3b4b5, - 0xaabbccddeeff, - 0x4d3a99c351dd, - 0x1a982c7e459a, - 0xd3f7d3f7d3f7, - 0x714c5c886e97, - 0x587ee5f9350f, - 0xa0478cc39091, - 0x533cb6c723f6, - 0x8fd0a4f256e9 - }; - int defaultKeysSize = sizeof(defaultKeys) / sizeof(uint64_t); - - for (int defaultKeyCounter = 0; defaultKeyCounter < defaultKeysSize; defaultKeyCounter++) - { - num_to_bytes(defaultKeys[defaultKeyCounter], 6, (uint8_t*)(keyBlock + defaultKeyCounter * 6)); + int defaultKeysSize = MifareDefaultKeysSize; + for (int defaultKeyCounter = 0; defaultKeyCounter < defaultKeysSize; defaultKeyCounter++){ + num_to_bytes(MifareDefaultKeys[defaultKeyCounter], 6, (uint8_t*)(keyBlock + defaultKeyCounter * 6)); } - + if (param_getchar(Cmd, 0)=='*') { - blockNo = 3; - switch(param_getchar(Cmd+1, 0)) { - case '0': SectorsCnt = 5; break; - case '1': SectorsCnt = 16; break; - case '2': SectorsCnt = 32; break; - case '4': SectorsCnt = 40; break; - default: SectorsCnt = 16; + SectorsCnt = ParamCardSizeSectors(param_getchar(Cmd + 1, 0)); + } else { + blockNo = param_get8(Cmd, 0); + // Singe Key check, so Set Sector count to cover sectors (1 to sector that contains the block) + // 1 and 2 Cards : Sector = blockNo/4 + 1 + // Sectors 0 - 31 : 4 blocks per sector : Blocks 0 - 127 + // Sectors 32 - 39 : 16 blocks per sector : Blocks 128 - 255 (4K) + if (blockNo < 128) { + SectorsCnt = (blockNo / 4) + 1; + } else { + SectorsCnt = 32 + ((blockNo-128)/16) + 1; } + singleBlock = true; // Set flag for single key check } - else - blockNo = param_get8(Cmd, 0); - + ctmp = param_getchar(Cmd, 1); - switch (ctmp) { - case 'a': case 'A': - keyType = !0; - break; - case 'b': case 'B': - keyType = !1; - break; - case '?': - keyType = 2; - break; - default: - PrintAndLog("Key type must be A , B or ?"); + clen = param_getlength(Cmd, 1); + if (clen == 1) { + switch (ctmp) { + case 'a': case 'A': + keyType = 0; + break; + case 'b': case 'B': + keyType = 1; + break; + case '?': + keyType = 2; + break; + default: + PrintAndLog("Key type must be A , B or ?"); + free(keyBlock); + return 1; + }; + } + + parseParamTDS(Cmd, 2, &transferToEml, &createDumpFile, &timeout14a); + + if (singleBlock & createDumpFile) { + PrintAndLog (" block key check () and write to dump file (d) combination is not supported "); + PrintAndLog (" please remove option d and try again"); return 1; - }; - - ctmp = param_getchar(Cmd, 2); - if (ctmp == 't' || ctmp == 'T') transferToEml = 1; - else if (ctmp == 'd' || ctmp == 'D') createDumpFile = 1; - - for (i = transferToEml || createDumpFile; param_getchar(Cmd, 2 + i); i++) { + } + + param3InUse = transferToEml | createDumpFile | (timeout14a != MF_CHKKEYS_DEFTIMEOUT); + + PrintAndLog("--chk keys. sectors:%2d, block no:%3d, key type:%c, eml:%c, dmp=%c checktimeout=%d us", + SectorsCnt, blockNo, keyType==0?'A':keyType==1?'B':'?', transferToEml?'y':'n', createDumpFile?'y':'n', ((uint32_t)timeout14a * 1000) / 106); + + for (i = param3InUse; param_getchar(Cmd, 2 + i); i++) { if (!param_gethex(Cmd, 2 + i, keyBlock + 6 * keycnt, 12)) { if ( stKeyBlock - keycnt < 2) { p = realloc(keyBlock, 6*(stKeyBlock+=10)); @@ -866,34 +1158,40 @@ int CmdHF14AMfChk(const char *Cmd) keyBlock = p; } PrintAndLog("chk key[%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], 6); + (keyBlock + 6*keycnt)[0], (keyBlock + 6*keycnt)[1], (keyBlock + 6*keycnt)[2], + (keyBlock + 6*keycnt)[3], (keyBlock + 6*keycnt)[4], (keyBlock + 6*keycnt)[5], 6); keycnt++; } else { // May be a dic file - if ( param_getstr(Cmd, 2 + i,filename) >= FILE_PATH_SIZE ) { + if ( param_getstr(Cmd, 2 + i, filename, sizeof(filename)) >= FILE_PATH_SIZE ) { PrintAndLog("File name too long"); free(keyBlock); return 2; } - - if ( (f = fopen( filename , "r")) ) { - while( fgets(buf, sizeof(buf), f) ){ + + if ((f = fopen( filename , "r"))) { + 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 - if (!isxdigit(buf[0])){ - PrintAndLog("File content error. '%s' must include 12 HEX symbols",buf); + if( buf[0]=='#' ) continue; //The line start with # is comment, skip + + bool content_error = false; + for (int i = 0; i < 12; i++) { + if (!isxdigit((unsigned char)buf[i])) { + content_error = true; + } + } + if (content_error) { + PrintAndLog("File content error. '%s' must include 12 HEX symbols", buf); continue; } - + buf[12] = 0; - if ( stKeyBlock - keycnt < 2) { + if (stKeyBlock - keycnt < 2) { p = realloc(keyBlock, 6*(stKeyBlock+=10)); if (!p) { PrintAndLog("Cannot allocate memory for defKeys"); @@ -914,90 +1212,158 @@ int CmdHF14AMfChk(const char *Cmd) PrintAndLog("File: %s: not found or locked.", filename); free(keyBlock); return 1; - } } } - + + // fill with default keys if (keycnt == 0) { PrintAndLog("No key specified, trying default keys"); for (;keycnt < defaultKeysSize; keycnt++) PrintAndLog("chk default key[%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], 6); + (keyBlock + 6*keycnt)[0], (keyBlock + 6*keycnt)[1], (keyBlock + 6*keycnt)[2], + (keyBlock + 6*keycnt)[3], (keyBlock + 6*keycnt)[4], (keyBlock + 6*keycnt)[5], 6); } - + // initialize storage for found keys - bool validKey[2][40]; - uint8_t foundKey[2][40][6]; - for (uint16_t t = 0; t < 2; t++) { + e_sector = calloc(SectorsCnt, sizeof(sector_t)); + if (e_sector == NULL) { + free(keyBlock); + return 1; + } + for (uint8_t keyAB = 0; keyAB < 2; keyAB++) { for (uint16_t sectorNo = 0; sectorNo < SectorsCnt; sectorNo++) { - validKey[t][sectorNo] = false; - for (uint16_t i = 0; i < 6; i++) { - foundKey[t][sectorNo][i] = 0xff; - } + e_sector[sectorNo].Key[keyAB] = 0xffffffffffff; + e_sector[sectorNo].foundKey[keyAB] = 0; } } - - for ( int t = !keyType; t < 2; keyType==2?(t++):(t=2) ) { - int b=blockNo; - for (int i = 0; i < SectorsCnt; ++i) { - PrintAndLog("--sector:%2d, block:%3d, key type:%C, key count:%2d ", i, b, t?'B':'A', keycnt); - uint32_t max_keys = keycnt>USB_CMD_DATA_SIZE/6?USB_CMD_DATA_SIZE/6:keycnt; - for (uint32_t c = 0; c < keycnt; c+=max_keys) { - uint32_t size = keycnt-c>max_keys?max_keys:keycnt-c; - res = mfCheckKeys(b, t, true, size, &keyBlock[6*c], &key64); - if (res != 1) { - if (!res) { - PrintAndLog("Found valid key:[%012" PRIx64 "]",key64); - num_to_bytes(key64, 6, foundKey[t][i]); - validKey[t][i] = true; - } + printf("\n"); + + bool foundAKey = false; + bool clearTraceLog = true; + uint32_t max_keys = keycnt > USB_CMD_DATA_SIZE / 6 ? USB_CMD_DATA_SIZE / 6 : keycnt; + + // !SingleKey, so all key check (if SectorsCnt > 0) + if (!singleBlock) { + PrintAndLog("To cancel this operation press the button on the proxmark..."); + printf("--"); + for (uint32_t c = 0; c < keycnt; c += max_keys) { + + uint32_t size = keycnt-c > max_keys ? max_keys : keycnt-c; + bool init = (c == 0); + bool drop_field = (c + size == keycnt); + res = mfCheckKeysSec(SectorsCnt, keyType, timeout14a, clearTraceLog, init, drop_field, size, &keyBlock[6 * c], e_sector); // timeout is (ms * 106)/10 or us*0.0106 + clearTraceLog = false; + + if (res != 1) { + if (!res) { + printf("o"); + foundAKey = true; } else { - PrintAndLog("Command execute timeout"); + printf("."); + } + } else { + printf("\n"); + PrintAndLog("Command execute timeout"); + } + } + } else { + int keyAB = keyType; + do { + res = mfCheckKeys(blockNo, keyAB & 0x01, timeout14a, true, keycnt, keyBlock, &key64); + clearTraceLog = false; + + if (res != 1) { + if (!res) { + // Use the common format below + // PrintAndLog("Found valid key:[%d:%c]%012" PRIx64, blockNo, (keyAB & 0x01)?'B':'A', key64); + foundAKey = true; + + // Store the Single Key for display list + // For a single block check, SectorsCnt = Sector that contains the block + e_sector[SectorsCnt-1].foundKey[(keyAB & 0x01)] = true; // flag key found + e_sector[SectorsCnt-1].Key[(keyAB & 0x01)] = key64; // Save key data + + } + } else { + PrintAndLog("Command execute timeout"); + } + } while(--keyAB > 0); + } + + // print result + if (foundAKey) { + PrintAndLog(""); + PrintAndLog("|---|----------------|----------------|"); + PrintAndLog("|sec|key A |key B |"); + PrintAndLog("|---|----------------|----------------|"); + for (i = 0; i < SectorsCnt; i++) { + // If a block key check, only print a line if a key was found. + if (!singleBlock || e_sector[i].foundKey[0] || e_sector[i].foundKey[1]) { + char keyAString[13] = " ? "; + char keyBString[13] = " ? "; + if (e_sector[i].foundKey[0]) { + sprintf(keyAString, "%012" PRIx64, e_sector[i].Key[0]); + } + if (e_sector[i].foundKey[1]) { + sprintf(keyBString, "%012" PRIx64, e_sector[i].Key[1]); } + PrintAndLog("|%03d| %s | %s |", i, keyAString, keyBString); } - b<127?(b+=4):(b+=16); } + PrintAndLog("|---|----------------|----------------|"); + } else { + PrintAndLog(""); + PrintAndLog("No valid keys found."); } if (transferToEml) { uint8_t block[16]; for (uint16_t sectorNo = 0; sectorNo < SectorsCnt; sectorNo++) { - if (validKey[0][sectorNo] || validKey[1][sectorNo]) { + if (e_sector[sectorNo].foundKey[0] || e_sector[sectorNo].foundKey[1]) { mfEmlGetMem(block, FirstBlockOfSector(sectorNo) + NumBlocksPerSector(sectorNo) - 1, 1); for (uint16_t t = 0; t < 2; t++) { - if (validKey[t][sectorNo]) { - memcpy(block + t*10, foundKey[t][sectorNo], 6); + if (e_sector[sectorNo].foundKey[t]) { + num_to_bytes(e_sector[sectorNo].Key[t], 6, block + t * 10); + keyFoundCount++; // Key found count for information } } mfEmlSetMem(block, FirstBlockOfSector(sectorNo) + NumBlocksPerSector(sectorNo) - 1, 1); } } - PrintAndLog("Found keys have been transferred to the emulator memory"); + // Updated to show the actual number of keys found/transfered. + PrintAndLog("%d keys(s) found have been transferred to the emulator memory",keyFoundCount); } - if (createDumpFile) { + if (createDumpFile && !singleBlock) { FILE *fkeys = fopen("dumpkeys.bin","wb"); - if (fkeys == NULL) { + if (fkeys == NULL) { PrintAndLog("Could not create file dumpkeys.bin"); + free(e_sector); free(keyBlock); return 1; } - for (uint16_t t = 0; t < 2; t++) { - fwrite(foundKey[t], 1, 6*SectorsCnt, fkeys); + uint8_t mkey[6]; + for (uint8_t t = 0; t < 2; t++) { + for (uint8_t sectorNo = 0; sectorNo < SectorsCnt; sectorNo++) { + num_to_bytes(e_sector[sectorNo].Key[t], 6, mkey); + fwrite(mkey, 1, 6, fkeys); + } } fclose(fkeys); PrintAndLog("Found keys have been dumped to file dumpkeys.bin. 0xffffffffffff has been inserted for unknown keys."); } + free(e_sector); free(keyBlock); PrintAndLog(""); return 0; } + void readerAttack(nonces_t ar_resp[], bool setEmulatorMem, bool doStandardAttack) { - #define ATTACK_KEY_COUNT 8 // keep same as define in iso14443a.c -> Mifare1ksim() + #define ATTACK_KEY_COUNT 7 // keep same as define in iso14443a.c -> Mifare1ksim() + // cannot be more than 7 or it will overrun c.d.asBytes(512) uint64_t key = 0; typedef struct { uint64_t keyA; @@ -1006,7 +1372,7 @@ void readerAttack(nonces_t ar_resp[], bool setEmulatorMem, bool doStandardAttack st_t sector_trailer[ATTACK_KEY_COUNT]; memset(sector_trailer, 0x00, sizeof(sector_trailer)); - uint8_t stSector[ATTACK_KEY_COUNT]; + uint8_t stSector[ATTACK_KEY_COUNT]; memset(stSector, 0x00, sizeof(stSector)); uint8_t key_cnt[ATTACK_KEY_COUNT]; memset(key_cnt, 0x00, sizeof(key_cnt)); @@ -1069,7 +1435,7 @@ void readerAttack(nonces_t ar_resp[], bool setEmulatorMem, bool doStandardAttack if (setEmulatorMem) { for (uint8_t i = 0; i0) { - uint8_t memBlock[16]; + uint8_t memBlock[16]; memset(memBlock, 0x00, sizeof(memBlock)); char cmd1[36]; memset(cmd1,0x00,sizeof(cmd1)); @@ -1079,11 +1445,11 @@ void readerAttack(nonces_t ar_resp[], bool setEmulatorMem, bool doStandardAttack PrintAndLog("block data must include 32 HEX symbols"); return; } - + UsbCommand c = {CMD_MIFARE_EML_MEMSET, {(stSector[i]*4+3), 1, 0}}; memcpy(c.d.asBytes, memBlock, 16); clearCommandBuffer(); - SendCommand(&c); + SendCommand(&c); } } } @@ -1098,11 +1464,12 @@ void readerAttack(nonces_t ar_resp[], bool setEmulatorMem, bool doStandardAttack }*/ } -int usage_hf14_mf1ksim(void) { - PrintAndLog("Usage: hf mf sim h u n i x"); +int usage_hf14_mfsim(void) { + PrintAndLog("Usage: hf mf sim [h] [*] [u ] [n ] [i] [x]"); PrintAndLog("options:"); - PrintAndLog(" h this help"); - PrintAndLog(" u (Optional) UID 4,7 or 10 bytes. If not specified, the UID 4B from emulator memory will be used"); + PrintAndLog(" h (Optional) this help"); + PrintAndLog(" card memory: 0 - MINI(320 bytes), 1 - 1K, 2 - 2K, 4 - 4K, - 1K"); + PrintAndLog(" u (Optional) UID 4 or 7 bytes. If not specified, the UID 4B from emulator memory will be used"); PrintAndLog(" n (Optional) Automatically exit simulation after blocks have been read by reader. 0 = infinite"); PrintAndLog(" i (Optional) Interactive, means that console will not be returned until simulation finishes or is aborted"); PrintAndLog(" x (Optional) Crack, performs the 'reader attack', nr/ar attack against a legitimate reader, fishes out the key(s)"); @@ -1111,21 +1478,20 @@ int usage_hf14_mf1ksim(void) { PrintAndLog(" r (Optional) Generate random nonces instead of sequential nonces. Standard reader attack won't work with this option, only moebius attack works."); PrintAndLog("samples:"); PrintAndLog(" hf mf sim u 0a0a0a0a"); + PrintAndLog(" hf mf sim *4"); PrintAndLog(" hf mf sim u 11223344556677"); - PrintAndLog(" hf mf sim u 112233445566778899AA"); PrintAndLog(" hf mf sim f uids.txt"); PrintAndLog(" hf mf sim u 0a0a0a0a e"); - + return 0; } -int CmdHF14AMf1kSim(const char *Cmd) { +int CmdHF14AMfSim(const char *Cmd) { UsbCommand resp; - uint8_t uid[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + uint8_t uid[7] = {0}; uint8_t exitAfterNReads = 0; uint8_t flags = 0; int uidlen = 0; - uint8_t pnr = 0; bool setEmulatorMem = false; bool attackFromFile = false; FILE *f; @@ -1136,9 +1502,21 @@ int CmdHF14AMf1kSim(const char *Cmd) { uint8_t cmdp = 0; bool errors = false; + uint8_t cardsize = '1'; while(param_getchar(Cmd, cmdp) != 0x00) { switch(param_getchar(Cmd, cmdp)) { + case '*': + cardsize = param_getchar(Cmd + 1, cmdp); + switch(cardsize) { + case '0': + case '1': + case '2': + case '4': break; + default: cardsize = '1'; + } + cmdp++; + break; case 'e': case 'E': setEmulatorMem = true; @@ -1149,7 +1527,7 @@ int CmdHF14AMf1kSim(const char *Cmd) { break; case 'f': case 'F': - len = param_getstr(Cmd, cmdp+1, filename); + len = param_getstr(Cmd, cmdp+1, filename, sizeof(filename)); if (len < 1) { PrintAndLog("error no filename found"); return 0; @@ -1162,7 +1540,7 @@ int CmdHF14AMf1kSim(const char *Cmd) { break; case 'h': case 'H': - return usage_hf14_mf1ksim(); + return usage_hf14_mfsim(); case 'i': case 'I': flags |= FLAG_INTERACTIVE; @@ -1170,7 +1548,7 @@ int CmdHF14AMf1kSim(const char *Cmd) { break; case 'n': case 'N': - exitAfterNReads = param_get8(Cmd, pnr+1); + exitAfterNReads = param_get8(Cmd, cmdp+1); cmdp += 2; break; case 'r': @@ -1180,12 +1558,14 @@ int CmdHF14AMf1kSim(const char *Cmd) { break; case 'u': case 'U': - param_gethex_ex(Cmd, cmdp+1, uid, &uidlen); - switch(uidlen) { - case 20: flags = FLAG_10B_UID_IN_DATA; break; //not complete + uidlen = 14; + if (param_gethex_ex(Cmd, cmdp+1, uid, &uidlen)) { + return usage_hf14_mfsim(); + } + switch (uidlen) { case 14: flags = FLAG_7B_UID_IN_DATA; break; case 8: flags = FLAG_4B_UID_IN_DATA; break; - default: return usage_hf14_mf1ksim(); + default: return usage_hf14_mfsim(); } cmdp += 2; break; @@ -1202,7 +1582,7 @@ int CmdHF14AMf1kSim(const char *Cmd) { if(errors) break; } //Validations - if(errors) return usage_hf14_mf1ksim(); + if(errors) return usage_hf14_mfsim(); //get uid from file if (attackFromFile) { @@ -1218,9 +1598,9 @@ int CmdHF14AMf1kSim(const char *Cmd) { memset(buf, 0, sizeof(buf)); memset(uid, 0, sizeof(uid)); - if (fgets(buf, sizeof(buf), f) == NULL) { + if (fgets(buf, sizeof(buf), f) == NULL) { if (count > 0) break; - + PrintAndLog("File reading error."); fclose(f); return 2; @@ -1229,10 +1609,9 @@ int CmdHF14AMf1kSim(const char *Cmd) { uidlen = strlen(buf)-1; switch(uidlen) { - case 20: flags |= FLAG_10B_UID_IN_DATA; break; //not complete case 14: flags |= FLAG_7B_UID_IN_DATA; break; case 8: flags |= FLAG_4B_UID_IN_DATA; break; - default: + default: PrintAndLog("uid in file wrong length at %d (length: %d) [%s]",count, uidlen, buf); fclose(f); return 2; @@ -1241,19 +1620,23 @@ int CmdHF14AMf1kSim(const char *Cmd) { for (uint8_t i = 0; i < uidlen; i += 2) { sscanf(&buf[i], "%02x", (unsigned int *)&uid[i / 2]); } - - PrintAndLog("mf 1k sim uid: %s, numreads:%d, flags:%d (0x%02x) - press button to abort", - flags & FLAG_4B_UID_IN_DATA ? sprint_hex(uid,4): - flags & FLAG_7B_UID_IN_DATA ? sprint_hex(uid,7): - flags & FLAG_10B_UID_IN_DATA ? sprint_hex(uid,10): "N/A" - , exitAfterNReads, flags, flags); - - UsbCommand c = {CMD_SIMULATE_MIFARE_CARD, {flags, exitAfterNReads,0}}; + + PrintAndLog("mf sim cardsize: %s, uid: %s, numreads:%d, flags:%d (0x%02x) - press button to abort", + cardsize == '0' ? "Mini" : + cardsize == '2' ? "2K" : + cardsize == '4' ? "4K" : "1K", + flags & FLAG_4B_UID_IN_DATA ? sprint_hex(uid,4): + flags & FLAG_7B_UID_IN_DATA ? sprint_hex(uid,7): "N/A", + exitAfterNReads, + flags, + flags); + + UsbCommand c = {CMD_SIMULATE_MIFARE_CARD, {flags, exitAfterNReads, cardsize}}; memcpy(c.d.asBytes, uid, sizeof(uid)); clearCommandBuffer(); SendCommand(&c); - while(! WaitForResponseTimeout(CMD_ACK,&resp,1500)) { + while (! WaitForResponseTimeout(CMD_ACK,&resp,1500)) { //We're waiting only 1.5 s at a time, otherwise we get the // annoying message about "Waiting for a response... " } @@ -1270,22 +1653,27 @@ int CmdHF14AMf1kSim(const char *Cmd) { count++; } fclose(f); - } else { //not from file - PrintAndLog("mf 1k sim uid: %s, numreads:%d, flags:%d (0x%02x) ", - flags & FLAG_4B_UID_IN_DATA ? sprint_hex(uid,4): - flags & FLAG_7B_UID_IN_DATA ? sprint_hex(uid,7): - flags & FLAG_10B_UID_IN_DATA ? sprint_hex(uid,10): "N/A" - , exitAfterNReads, flags, flags); + } else { //not from file - UsbCommand c = {CMD_SIMULATE_MIFARE_CARD, {flags, exitAfterNReads,0}}; + PrintAndLog("mf sim cardsize: %s, uid: %s, numreads:%d, flags:%d (0x%02x) ", + cardsize == '0' ? "Mini" : + cardsize == '2' ? "2K" : + cardsize == '4' ? "4K" : "1K", + flags & FLAG_4B_UID_IN_DATA ? sprint_hex(uid,4): + flags & FLAG_7B_UID_IN_DATA ? sprint_hex(uid,7): "N/A", + exitAfterNReads, + flags, + flags); + + UsbCommand c = {CMD_SIMULATE_MIFARE_CARD, {flags, exitAfterNReads, cardsize}}; memcpy(c.d.asBytes, uid, sizeof(uid)); clearCommandBuffer(); SendCommand(&c); if(flags & FLAG_INTERACTIVE) { PrintAndLog("Press pm3-button to abort simulation"); - while(! WaitForResponseTimeout(CMD_ACK,&resp,1500)) { + while(! WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { //We're waiting only 1.5 s at a time, otherwise we get the // annoying message about "Waiting for a response... " } @@ -1318,12 +1706,12 @@ int CmdHF14AMfDbg(const char *Cmd) PrintAndLog(" 4 - print even debug messages in timing critical functions"); PrintAndLog(" Note: this option therefore may cause malfunction itself"); return 0; - } + } - UsbCommand c = {CMD_MIFARE_SET_DBGMODE, {dbgMode, 0, 0}}; - SendCommand(&c); + UsbCommand c = {CMD_MIFARE_SET_DBGMODE, {dbgMode, 0, 0}}; + SendCommand(&c); - return 0; + return 0; } int CmdHF14AMfEGet(const char *Cmd) @@ -1335,8 +1723,8 @@ int CmdHF14AMfEGet(const char *Cmd) PrintAndLog("Usage: hf mf eget "); PrintAndLog(" sample: hf mf eget 0 "); return 0; - } - + } + blockNo = param_get8(Cmd, 0); PrintAndLog(" "); @@ -1346,7 +1734,7 @@ int CmdHF14AMfEGet(const char *Cmd) PrintAndLog("Command execute timeout"); } - return 0; + return 0; } int CmdHF14AMfEClear(const char *Cmd) @@ -1355,11 +1743,11 @@ int CmdHF14AMfEClear(const char *Cmd) PrintAndLog("Usage: hf mf eclr"); PrintAndLog("It set card emulator memory to empty data blocks and key A/B FFFFFFFFFFFF \n"); return 0; - } + } - UsbCommand c = {CMD_MIFARE_EML_MEMCLR, {0, 0, 0}}; - SendCommand(&c); - return 0; + UsbCommand c = {CMD_MIFARE_EML_MEMCLR, {0, 0, 0}}; + SendCommand(&c); + return 0; } @@ -1374,20 +1762,17 @@ int CmdHF14AMfESet(const char *Cmd) PrintAndLog("Usage: hf mf eset "); PrintAndLog(" sample: hf mf eset 1 000102030405060708090a0b0c0d0e0f "); return 0; - } - + } + blockNo = param_get8(Cmd, 0); - + if (param_gethex(Cmd, 1, memBlock, 32)) { PrintAndLog("block data must include 32 HEX symbols"); return 1; } - + // 1 - blocks count - UsbCommand c = {CMD_MIFARE_EML_MEMSET, {blockNo, 1, 0}}; - memcpy(c.d.asBytes, memBlock, 16); - SendCommand(&c); - return 0; + return mfEmlSetMem(memBlock, blockNo, 1); } @@ -1400,9 +1785,9 @@ int CmdHF14AMfELoad(const char *Cmd) uint8_t buf8[64] = {0x00}; int i, len, blockNum, numBlocks; int nameParamNo = 1; - + char ctmp = param_getchar(Cmd, 0); - + if ( ctmp == 'h' || ctmp == 0x00) { PrintAndLog("It loads emul dump from the file `filename.eml`"); PrintAndLog("Usage: hf mf eload [card memory] "); @@ -1411,11 +1796,11 @@ int CmdHF14AMfELoad(const char *Cmd) PrintAndLog(" sample: hf mf eload filename"); PrintAndLog(" hf mf eload 4 filename"); return 0; - } + } switch (ctmp) { case '0' : numBlocks = 5*4; break; - case '1' : + case '1' : case '\0': numBlocks = 16*4; break; case '2' : numBlocks = 32*4; break; case '4' : numBlocks = 256; break; @@ -1425,34 +1810,34 @@ int CmdHF14AMfELoad(const char *Cmd) } } - len = param_getstr(Cmd,nameParamNo,filename); - + len = param_getstr(Cmd, nameParamNo, filename, sizeof(filename)); + if (len > FILE_PATH_SIZE - 5) len = FILE_PATH_SIZE - 5; fnameptr += len; - sprintf(fnameptr, ".eml"); - + sprintf(fnameptr, ".eml"); + // open file f = fopen(filename, "r"); if (f == NULL) { PrintAndLog("File %s not found or locked", filename); return 1; } - + blockNum = 0; while(!feof(f)){ memset(buf, 0, sizeof(buf)); - + if (fgets(buf, sizeof(buf), f) == NULL) { - + if (blockNum >= numBlocks) break; - + PrintAndLog("File reading error."); fclose(f); return 2; } - + if (strlen(buf) < 32){ if(strlen(buf) && feof(f)) break; @@ -1460,11 +1845,11 @@ int CmdHF14AMfELoad(const char *Cmd) fclose(f); return 2; } - + for (i = 0; i < 32; i += 2) { sscanf(&buf[i], "%02x", (unsigned int *)&buf8[i / 2]); } - + if (mfEmlSetMem(buf8, blockNum, 1)) { PrintAndLog("Cant set emul block: %3d", blockNum); fclose(f); @@ -1472,12 +1857,12 @@ int CmdHF14AMfELoad(const char *Cmd) } printf("."); blockNum++; - + if (blockNum >= numBlocks) break; } fclose(f); printf("\n"); - + if ((blockNum != numBlocks)) { PrintAndLog("File content error. Got %d must be %d blocks.",blockNum, numBlocks); return 4; @@ -1495,12 +1880,12 @@ int CmdHF14AMfESave(const char *Cmd) uint8_t buf[64]; int i, j, len, numBlocks; int nameParamNo = 1; - + memset(filename, 0, sizeof(filename)); memset(buf, 0, sizeof(buf)); char ctmp = param_getchar(Cmd, 0); - + if ( ctmp == 'h' || ctmp == 'H') { PrintAndLog("It saves emul dump into the file `filename.eml` or `cardID.eml`"); PrintAndLog(" Usage: hf mf esave [card memory] [file name w/o `.eml`]"); @@ -1510,11 +1895,11 @@ int CmdHF14AMfESave(const char *Cmd) PrintAndLog(" hf mf esave 4"); PrintAndLog(" hf mf esave 4 filename"); return 0; - } + } switch (ctmp) { case '0' : numBlocks = 5*4; break; - case '1' : + case '1' : case '\0': numBlocks = 16*4; break; case '2' : numBlocks = 32*4; break; case '4' : numBlocks = 256; break; @@ -1524,10 +1909,10 @@ int CmdHF14AMfESave(const char *Cmd) } } - len = param_getstr(Cmd,nameParamNo,filename); - + len = param_getstr(Cmd,nameParamNo,filename,sizeof(filename)); + if (len > FILE_PATH_SIZE - 5) len = FILE_PATH_SIZE - 5; - + // user supplied filename? if (len < 1) { // get filename (UID from memory) @@ -1545,8 +1930,8 @@ int CmdHF14AMfESave(const char *Cmd) } // add file extension - sprintf(fnameptr, ".eml"); - + sprintf(fnameptr, ".eml"); + // open file f = fopen(filename, "w+"); @@ -1554,7 +1939,7 @@ int CmdHF14AMfESave(const char *Cmd) PrintAndLog("Can't open file %s ", filename); return 1; } - + // put hex for (i = 0; i < numBlocks; i++) { if (mfEmlGetMem(buf, i, 1)) { @@ -1562,14 +1947,14 @@ int CmdHF14AMfESave(const char *Cmd) break; } for (j = 0; j < 16; j++) - fprintf(f, "%02X", buf[j]); + fprintf(f, "%02X", buf[j]); fprintf(f,"\n"); } fclose(f); - + PrintAndLog("Saved %d blocks to file: %s", numBlocks, filename); - - return 0; + + return 0; } @@ -1577,7 +1962,7 @@ int CmdHF14AMfECFill(const char *Cmd) { uint8_t keyType = 0; uint8_t numSectors = 16; - + if (strlen(Cmd) < 1 || param_getchar(Cmd, 0) == 'h') { PrintAndLog("Usage: hf mf ecfill [card memory]"); PrintAndLog(" [card memory]: 0 = 320 bytes (Mifare Mini), 1 = 1K (default), 2 = 2K, 4 = 4K"); @@ -1587,7 +1972,7 @@ int CmdHF14AMfECFill(const char *Cmd) PrintAndLog("Read card and transfer its data to emulator memory."); PrintAndLog("Keys must be laid in the emulator memory. \n"); return 0; - } + } char ctmp = param_getchar(Cmd, 0); if (ctmp != 'a' && ctmp != 'A' && ctmp != 'b' && ctmp != 'B') { @@ -1599,14 +1984,14 @@ int CmdHF14AMfECFill(const char *Cmd) ctmp = param_getchar(Cmd, 1); switch (ctmp) { case '0' : numSectors = 5; break; - case '1' : + case '1' : case '\0': numSectors = 16; break; case '2' : numSectors = 32; break; case '4' : numSectors = 40; break; default: numSectors = 16; - } + } - printf("--params: numSectors: %d, keyType:%d", numSectors, keyType); + printf("--params: numSectors: %d, keyType:%d\n", numSectors, keyType); UsbCommand c = {CMD_MIFARE_EML_CARDLOAD, {numSectors, keyType, 0}}; SendCommand(&c); return 0; @@ -1616,30 +2001,35 @@ int CmdHF14AMfECFill(const char *Cmd) int CmdHF14AMfEKeyPrn(const char *Cmd) { int i; - uint8_t numSectors; + uint8_t numSectors = 16; uint8_t data[16]; uint64_t keyA, keyB; - + bool createDumpFile = false; + if (param_getchar(Cmd, 0) == 'h') { PrintAndLog("It prints the keys loaded in the emulator memory"); - PrintAndLog("Usage: hf mf ekeyprn [card memory]"); + PrintAndLog("Usage: hf mf ekeyprn [card memory] [d]"); PrintAndLog(" [card memory]: 0 = 320 bytes (Mifare Mini), 1 = 1K (default), 2 = 2K, 4 = 4K"); + PrintAndLog(" [d] : write keys to binary file dumpkeys.bin"); PrintAndLog(""); PrintAndLog(" sample: hf mf ekeyprn 1"); return 0; - } + } + + uint8_t cmdp = 0; + while (param_getchar(Cmd, cmdp) != 0x00) { + switch (param_getchar(Cmd, cmdp)) { + case '0' : numSectors = 5; break; + case '1' : + case '\0': numSectors = 16; break; + case '2' : numSectors = 32; break; + case '4' : numSectors = 40; break; + case 'd' : + case 'D' : createDumpFile = true; break; + } + cmdp++; + } - char cmdp = param_getchar(Cmd, 0); - - switch (cmdp) { - case '0' : numSectors = 5; break; - case '1' : - case '\0': numSectors = 16; break; - case '2' : numSectors = 32; break; - case '4' : numSectors = 40; break; - default: numSectors = 16; - } - PrintAndLog("|---|----------------|----------------|"); PrintAndLog("|sec|key A |key B |"); PrintAndLog("|---|----------------|----------------|"); @@ -1653,86 +2043,178 @@ int CmdHF14AMfEKeyPrn(const char *Cmd) PrintAndLog("|%03d| %012" PRIx64 " | %012" PRIx64 " |", i, keyA, keyB); } PrintAndLog("|---|----------------|----------------|"); - + + // Create dump file + if (createDumpFile) { + FILE *fkeys; + if ((fkeys = fopen("dumpkeys.bin","wb")) == NULL) { + PrintAndLog("Could not create file dumpkeys.bin"); + return 1; + } + PrintAndLog("Printing keys to binary file dumpkeys.bin..."); + for(i = 0; i < numSectors; i++) { + if (mfEmlGetMem(data, FirstBlockOfSector(i) + NumBlocksPerSector(i) - 1, 1)) { + PrintAndLog("error get block %d", FirstBlockOfSector(i) + NumBlocksPerSector(i) - 1); + break; + } + fwrite(data, 1, 6, fkeys); + } + for(i = 0; i < numSectors; i++) { + if (mfEmlGetMem(data, FirstBlockOfSector(i) + NumBlocksPerSector(i) - 1, 1)) { + PrintAndLog("error get block %d", FirstBlockOfSector(i) + NumBlocksPerSector(i) - 1); + break; + } + fwrite(data+10, 1, 6, fkeys); + } + fclose(fkeys); + } + return 0; } int CmdHF14AMfCSetUID(const char *Cmd) { - uint8_t wipeCard = 0; uint8_t uid[8] = {0x00}; uint8_t oldUid[8] = {0x00}; uint8_t atqa[2] = {0x00}; uint8_t sak[1] = {0x00}; - uint8_t atqaPresent = 1; + uint8_t atqaPresent = 0; int res; - char ctmp; - int argi=0; - if (strlen(Cmd) < 1 || param_getchar(Cmd, argi) == 'h') { - PrintAndLog("Usage: hf mf csetuid [ATQA 4 hex symbols SAK 2 hex symbols] [w]"); - PrintAndLog("sample: hf mf csetuid 01020304"); - PrintAndLog("sample: hf mf csetuid 01020304 0004 08 w"); - PrintAndLog("Set UID, ATQA, and SAK for magic Chinese card (only works with such cards)"); - PrintAndLog("If you also want to wipe the card then add 'w' at the end of the command line."); - return 0; - } + uint8_t needHelp = 0; + char cmdp = 1; - if (param_getchar(Cmd, argi) && param_gethex(Cmd, argi, uid, 8)) { + if (param_getchar(Cmd, 0) && param_gethex(Cmd, 0, uid, 8)) { PrintAndLog("UID must include 8 HEX symbols"); return 1; } - argi++; - ctmp = param_getchar(Cmd, argi); - if (ctmp == 'w' || ctmp == 'W') { - wipeCard = 1; - atqaPresent = 0; - } + if (param_getlength(Cmd, 1) > 1 && param_getlength(Cmd, 2) > 1) { + atqaPresent = 1; + cmdp = 3; - if (atqaPresent) { - if (param_getchar(Cmd, argi)) { - if (param_gethex(Cmd, argi, atqa, 4)) { - PrintAndLog("ATQA must include 4 HEX symbols"); - return 1; - } - argi++; - if (!param_getchar(Cmd, argi) || param_gethex(Cmd, argi, sak, 2)) { - PrintAndLog("SAK must include 2 HEX symbols"); - return 1; - } - argi++; - } else - atqaPresent = 0; + if (param_gethex(Cmd, 1, atqa, 4)) { + PrintAndLog("ATQA must include 4 HEX symbols"); + return 1; + } + + if (param_gethex(Cmd, 2, sak, 2)) { + PrintAndLog("SAK must include 2 HEX symbols"); + return 1; + } } - if(!wipeCard) { - ctmp = param_getchar(Cmd, argi); - if (ctmp == 'w' || ctmp == 'W') { - wipeCard = 1; + while(param_getchar(Cmd, cmdp) != 0x00) + { + switch(param_getchar(Cmd, cmdp)) + { + case 'h': + case 'H': + needHelp = 1; + break; + default: + PrintAndLog("ERROR: Unknown parameter '%c'", param_getchar(Cmd, cmdp)); + needHelp = 1; + break; } + cmdp++; } - PrintAndLog("--wipe card:%s uid:%s", (wipeCard)?"YES":"NO", sprint_hex(uid, 4)); + if (strlen(Cmd) < 1 || needHelp) { + PrintAndLog(""); + PrintAndLog("Usage: hf mf csetuid [ATQA 4 hex symbols SAK 2 hex symbols]"); + PrintAndLog("sample: hf mf csetuid 01020304"); + PrintAndLog("sample: hf mf csetuid 01020304 0004 08"); + PrintAndLog("Set UID, ATQA, and SAK for magic Chinese card (only works with such cards)"); + return 0; + } - res = mfCSetUID(uid, (atqaPresent)?atqa:NULL, (atqaPresent)?sak:NULL, oldUid, wipeCard); + PrintAndLog("uid:%s", sprint_hex(uid, 4)); + if (atqaPresent) { + PrintAndLog("--atqa:%s sak:%02x", sprint_hex(atqa, 2), sak[0]); + } + + res = mfCSetUID(uid, (atqaPresent)?atqa:NULL, (atqaPresent)?sak:NULL, oldUid); if (res) { - PrintAndLog("Can't set UID. error=%d", res); + PrintAndLog("Can't set UID. Error=%d", res); return 1; } - + PrintAndLog("old UID:%s", sprint_hex(oldUid, 4)); PrintAndLog("new UID:%s", sprint_hex(uid, 4)); return 0; } +int CmdHF14AMfCWipe(const char *Cmd) +{ + int res, gen = 0; + int numBlocks = 16 * 4; + bool wipeCard = false; + bool fillCard = false; + + if (strlen(Cmd) < 1 || param_getchar(Cmd, 0) == 'h') { + PrintAndLog("Usage: hf mf cwipe [card size] [w] [f]"); + PrintAndLog("sample: hf mf cwipe 1 w f"); + PrintAndLog("[card size]: 0 = 320 bytes (Mifare Mini), 1 = 1K (default), 2 = 2K, 4 = 4K"); + PrintAndLog("w - Wipe magic Chinese card (only works with gen:1a cards)"); + PrintAndLog("f - Fill the card with default data and keys (works with gen:1a and gen:1b cards only)"); + return 0; + } + + gen = mfCIdentify(); + if ((gen != 1) && (gen != 2)) + return 1; + + numBlocks = ParamCardSizeBlocks(param_getchar(Cmd, 0)); + + char cmdp = 0; + while(param_getchar(Cmd, cmdp) != 0x00){ + switch(param_getchar(Cmd, cmdp)) { + case 'w': + case 'W': + wipeCard = 1; + break; + case 'f': + case 'F': + fillCard = 1; + break; + default: + break; + } + cmdp++; + } + + if (!wipeCard && !fillCard) + wipeCard = true; + + PrintAndLog("--blocks count:%2d wipe:%c fill:%c", numBlocks, (wipeCard)?'y':'n', (fillCard)?'y':'n'); + + if (gen == 2) { + /* generation 1b magic card */ + if (wipeCard) { + PrintAndLog("WARNING: can't wipe magic card 1b generation"); + } + res = mfCWipe(numBlocks, true, false, fillCard); + } else { + /* generation 1a magic card by default */ + res = mfCWipe(numBlocks, false, wipeCard, fillCard); + } + + if (res) { + PrintAndLog("Can't wipe. error=%d", res); + return 1; + } + PrintAndLog("OK"); + return 0; +} + int CmdHF14AMfCSetBlk(const char *Cmd) { uint8_t memBlock[16] = {0x00}; uint8_t blockNo = 0; bool wipeCard = false; - int res; + int res, gen = 0; if (strlen(Cmd) < 1 || param_getchar(Cmd, 0) == 'h') { PrintAndLog("Usage: hf mf csetblk [w]"); @@ -1740,7 +2222,11 @@ int CmdHF14AMfCSetBlk(const char *Cmd) PrintAndLog("Set block data for magic Chinese card (only works with such cards)"); PrintAndLog("If you also want wipe the card then add 'w' at the end of the command line"); return 0; - } + } + + gen = mfCIdentify(); + if ((gen != 1) && (gen != 2)) + return 1; blockNo = param_get8(Cmd, 0); @@ -1753,7 +2239,14 @@ int CmdHF14AMfCSetBlk(const char *Cmd) wipeCard = (ctmp == 'w' || ctmp == 'W'); PrintAndLog("--block number:%2d data:%s", blockNo, sprint_hex(memBlock, 16)); - res = mfCSetBlock(blockNo, memBlock, NULL, wipeCard, CSETBLOCK_SINGLE_OPER); + if (gen == 2) { + /* generation 1b magic card */ + res = mfCSetBlock(blockNo, memBlock, NULL, wipeCard, CSETBLOCK_SINGLE_OPER | CSETBLOCK_MAGIC_1B); + } else { + /* generation 1a magic card by default */ + res = mfCSetBlock(blockNo, memBlock, NULL, wipeCard, CSETBLOCK_SINGLE_OPER); + } + if (res) { PrintAndLog("Can't write block. error=%d", res); return 1; @@ -1767,33 +2260,44 @@ int CmdHF14AMfCLoad(const char *Cmd) FILE * f; char filename[FILE_PATH_SIZE] = {0x00}; char * fnameptr = filename; - char buf[64] = {0x00}; - uint8_t buf8[64] = {0x00}; + char buf[256] = {0x00}; + uint8_t buf8[256] = {0x00}; uint8_t fillFromEmulator = 0; - int i, len, blockNum, flags=0; - + int i, len, blockNum, flags = 0, gen = 0, numblock = 64; + if (param_getchar(Cmd, 0) == 'h' || param_getchar(Cmd, 0)== 0x00) { PrintAndLog("It loads magic Chinese card from the file `filename.eml`"); - PrintAndLog("or from emulator memory (option `e`)"); - PrintAndLog("Usage: hf mf cload "); - PrintAndLog(" or: hf mf cload e "); - PrintAndLog(" sample: hf mf cload filename"); + PrintAndLog("or from emulator memory (option `e`). 4K card: (option `4`)"); + PrintAndLog("Usage: hf mf cload [file name w/o `.eml`][e][4]"); + PrintAndLog(" or: hf mf cload e [4]"); + PrintAndLog("Sample: hf mf cload filename"); + PrintAndLog(" hf mf cload filname 4"); + PrintAndLog(" hf mf cload e"); + PrintAndLog(" hf mf cload e 4"); return 0; - } + } char ctmp = param_getchar(Cmd, 0); if (ctmp == 'e' || ctmp == 'E') fillFromEmulator = 1; - + ctmp = param_getchar(Cmd, 1); + if (ctmp == '4') numblock = 256; + + gen = mfCIdentify(); + PrintAndLog("Loading magic mifare %dK", numblock == 256 ? 4:1); + if (fillFromEmulator) { - for (blockNum = 0; blockNum < 16 * 4; blockNum += 1) { + for (blockNum = 0; blockNum < numblock; blockNum += 1) { if (mfEmlGetMem(buf8, blockNum, 1)) { PrintAndLog("Cant get block: %d", blockNum); return 2; } - if (blockNum == 0) flags = CSETBLOCK_INIT_FIELD + CSETBLOCK_WUPC; // switch on field and send magic sequence - if (blockNum == 1) flags = 0; // just write - if (blockNum == 16 * 4 - 1) flags = CSETBLOCK_HALT + CSETBLOCK_RESET_FIELD; // Done. Magic Halt and switch off field. + if (blockNum == 0) flags = CSETBLOCK_INIT_FIELD + CSETBLOCK_WUPC; // switch on field and send magic sequence + if (blockNum == 1) flags = 0; // just write + if (blockNum == numblock - 1) flags = CSETBLOCK_HALT + CSETBLOCK_RESET_FIELD; // Done. Magic Halt and switch off field. + if (gen == 2) + /* generation 1b magic card */ + flags |= CSETBLOCK_MAGIC_1B; if (mfCSetBlock(blockNum, buf8, NULL, 0, flags)) { PrintAndLog("Cant set magic card block: %d", blockNum); return 3; @@ -1801,26 +2305,28 @@ int CmdHF14AMfCLoad(const char *Cmd) } return 0; } else { - len = strlen(Cmd); + param_getstr(Cmd, 0, filename, sizeof(filename)); + + len = strlen(filename); if (len > FILE_PATH_SIZE - 5) len = FILE_PATH_SIZE - 5; - memcpy(filename, Cmd, len); + //memcpy(filename, Cmd, len); fnameptr += len; - sprintf(fnameptr, ".eml"); - + sprintf(fnameptr, ".eml"); + // open file f = fopen(filename, "r"); if (f == NULL) { PrintAndLog("File not found or locked."); return 1; } - + blockNum = 0; while(!feof(f)){ - + memset(buf, 0, sizeof(buf)); - + if (fgets(buf, sizeof(buf), f) == NULL) { fclose(f); PrintAndLog("File reading error."); @@ -1837,23 +2343,27 @@ int CmdHF14AMfCLoad(const char *Cmd) for (i = 0; i < 32; i += 2) sscanf(&buf[i], "%02x", (unsigned int *)&buf8[i / 2]); - if (blockNum == 0) flags = CSETBLOCK_INIT_FIELD + CSETBLOCK_WUPC; // switch on field and send magic sequence - if (blockNum == 1) flags = 0; // just write - if (blockNum == 16 * 4 - 1) flags = CSETBLOCK_HALT + CSETBLOCK_RESET_FIELD; // Done. Switch off field. + if (blockNum == 0) flags = CSETBLOCK_INIT_FIELD + CSETBLOCK_WUPC; // switch on field and send magic sequence + if (blockNum == 1) flags = 0; // just write + if (blockNum == numblock - 1) flags = CSETBLOCK_HALT + CSETBLOCK_RESET_FIELD; // Done. Switch off field. + if (gen == 2) + /* generation 1b magic card */ + flags |= CSETBLOCK_MAGIC_1B; if (mfCSetBlock(blockNum, buf8, NULL, 0, flags)) { PrintAndLog("Can't set magic card block: %d", blockNum); fclose(f); return 3; } blockNum++; - - if (blockNum >= 16 * 4) break; // magic card type - mifare 1K + + if (blockNum >= numblock) break; // magic card type - mifare 1K 64 blocks, mifare 4k 256 blocks } fclose(f); - - if (blockNum != 16 * 4 && blockNum != 32 * 4 + 8 * 16){ - PrintAndLog("File content error. There must be 64 blocks"); + + //if (blockNum != 16 * 4 && blockNum != 32 * 4 + 8 * 16){ + if (blockNum != numblock){ + PrintAndLog("File content error. There must be %d blocks", numblock); return 4; } PrintAndLog("Loaded from file: %s", filename); @@ -1865,7 +2375,7 @@ int CmdHF14AMfCLoad(const char *Cmd) int CmdHF14AMfCGetBlk(const char *Cmd) { uint8_t memBlock[16]; uint8_t blockNo = 0; - int res; + int res, gen = 0; memset(memBlock, 0x00, sizeof(memBlock)); if (strlen(Cmd) < 1 || param_getchar(Cmd, 0) == 'h') { @@ -1873,55 +2383,104 @@ int CmdHF14AMfCGetBlk(const char *Cmd) { PrintAndLog("sample: hf mf cgetblk 1"); PrintAndLog("Get block data from magic Chinese card (only works with such cards)\n"); return 0; - } + } + + gen = mfCIdentify(); blockNo = param_get8(Cmd, 0); PrintAndLog("--block number:%2d ", blockNo); - res = mfCGetBlock(blockNo, memBlock, CSETBLOCK_SINGLE_OPER); + if (gen == 2) { + /* generation 1b magic card */ + res = mfCGetBlock(blockNo, memBlock, CSETBLOCK_SINGLE_OPER | CSETBLOCK_MAGIC_1B); + } else { + /* generation 1a magic card by default */ + res = mfCGetBlock(blockNo, memBlock, CSETBLOCK_SINGLE_OPER); + } if (res) { PrintAndLog("Can't read block. error=%d", res); return 1; } - + PrintAndLog("block data:%s", sprint_hex(memBlock, 16)); + + if (mfIsSectorTrailer(blockNo)) { + PrintAndLogEx(NORMAL, "Trailer decoded:"); + PrintAndLogEx(NORMAL, "Key A: %s", sprint_hex_inrow(memBlock, 6)); + PrintAndLogEx(NORMAL, "Key B: %s", sprint_hex_inrow(&memBlock[10], 6)); + int bln = mfFirstBlockOfSector(mfSectorNum(blockNo)); + int blinc = (mfNumBlocksPerSector(mfSectorNum(blockNo)) > 4) ? 5 : 1; + for (int i = 0; i < 4; i++) { + PrintAndLogEx(NORMAL, "Access block %d%s: %s", bln, ((blinc > 1) && (i < 3) ? "+" : "") , mfGetAccessConditionsDesc(i, &memBlock[6])); + bln += blinc; + } + PrintAndLogEx(NORMAL, "UserData: %s", sprint_hex_inrow(&memBlock[9], 1)); + } + return 0; } - int CmdHF14AMfCGetSc(const char *Cmd) { uint8_t memBlock[16] = {0x00}; uint8_t sectorNo = 0; - int i, res, flags; + int i, res, flags, gen = 0, baseblock = 0, sect_size = 4; if (strlen(Cmd) < 1 || param_getchar(Cmd, 0) == 'h') { PrintAndLog("Usage: hf mf cgetsc "); PrintAndLog("sample: hf mf cgetsc 0"); PrintAndLog("Get sector data from magic Chinese card (only works with such cards)\n"); return 0; - } + } sectorNo = param_get8(Cmd, 0); - if (sectorNo > 15) { - PrintAndLog("Sector number must be in [0..15] as in MIFARE classic."); + + if (sectorNo > 39) { + PrintAndLog("Sector number must be in [0..15] in MIFARE classic 1k and [0..39] in MIFARE classic 4k."); return 1; } PrintAndLog("--sector number:%d ", sectorNo); + gen = mfCIdentify(); + flags = CSETBLOCK_INIT_FIELD + CSETBLOCK_WUPC; - for (i = 0; i < 4; i++) { + if (sectorNo < 32 ) { + baseblock = sectorNo * 4; + } else { + baseblock = 128 + 16 * (sectorNo - 32); + + } + if (sectorNo > 31) sect_size = 16; + + for (i = 0; i < sect_size; i++) { if (i == 1) flags = 0; - if (i == 3) flags = CSETBLOCK_HALT + CSETBLOCK_RESET_FIELD; + if (i == sect_size - 1) flags = CSETBLOCK_HALT + CSETBLOCK_RESET_FIELD; + + if (gen == 2) + /* generation 1b magic card */ + flags |= CSETBLOCK_MAGIC_1B; - res = mfCGetBlock(sectorNo * 4 + i, memBlock, flags); + res = mfCGetBlock(baseblock + i, memBlock, flags); if (res) { - PrintAndLog("Can't read block. %d error=%d", sectorNo * 4 + i, res); + PrintAndLog("Can't read block. %d error=%d", baseblock + i, res); return 1; } - - PrintAndLog("block %3d data:%s", sectorNo * 4 + i, sprint_hex(memBlock, 16)); + + PrintAndLog("block %3d data:%s", baseblock + i, sprint_hex(memBlock, 16)); + + if (mfIsSectorTrailer(baseblock + i)) { + PrintAndLogEx(NORMAL, "Trailer decoded:"); + PrintAndLogEx(NORMAL, "Key A: %s", sprint_hex_inrow(memBlock, 6)); + PrintAndLogEx(NORMAL, "Key B: %s", sprint_hex_inrow(&memBlock[10], 6)); + int bln = baseblock; + int blinc = (mfNumBlocksPerSector(sectorNo) > 4) ? 5 : 1; + for (int i = 0; i < 4; i++) { + PrintAndLogEx(NORMAL, "Access block %d%s: %s", bln, ((blinc > 1) && (i < 3) ? "+" : "") , mfGetAccessConditionsDesc(i, &memBlock[6])); + bln += blinc; + } + PrintAndLogEx(NORMAL, "UserData: %s", sprint_hex_inrow(&memBlock[9], 1)); + } } return 0; } @@ -1933,37 +2492,50 @@ int CmdHF14AMfCSave(const char *Cmd) { char filename[FILE_PATH_SIZE] = {0x00}; char * fnameptr = filename; uint8_t fillFromEmulator = 0; - uint8_t buf[64] = {0x00}; - int i, j, len, flags; - + uint8_t buf[256] = {0x00}; + int i, j, len, flags, gen = 0, numblock = 64; + // memset(filename, 0, sizeof(filename)); // memset(buf, 0, sizeof(buf)); if (param_getchar(Cmd, 0) == 'h') { PrintAndLog("It saves `magic Chinese` card dump into the file `filename.eml` or `cardID.eml`"); - PrintAndLog("or into emulator memory (option `e`)"); - PrintAndLog("Usage: hf mf esave [file name w/o `.eml`][e]"); - PrintAndLog(" sample: hf mf esave "); - PrintAndLog(" hf mf esave filename"); - PrintAndLog(" hf mf esave e \n"); + PrintAndLog("or into emulator memory (option `e`). 4K card: (option `4`)"); + PrintAndLog("Usage: hf mf csave [file name w/o `.eml`][e][4]"); + PrintAndLog("Sample: hf mf csave "); + PrintAndLog(" hf mf csave filename"); + PrintAndLog(" hf mf csave e"); + PrintAndLog(" hf mf csave 4"); + PrintAndLog(" hf mf csave filename 4"); + PrintAndLog(" hf mf csave e 4"); return 0; - } + } char ctmp = param_getchar(Cmd, 0); if (ctmp == 'e' || ctmp == 'E') fillFromEmulator = 1; + if (ctmp == '4') numblock = 256; + ctmp = param_getchar(Cmd, 1); + if (ctmp == '4') numblock = 256; + + gen = mfCIdentify(); + PrintAndLog("Saving magic mifare %dK", numblock == 256 ? 4:1); if (fillFromEmulator) { // put into emulator flags = CSETBLOCK_INIT_FIELD + CSETBLOCK_WUPC; - for (i = 0; i < 16 * 4; i++) { + for (i = 0; i < numblock; i++) { if (i == 1) flags = 0; - if (i == 16 * 4 - 1) flags = CSETBLOCK_HALT + CSETBLOCK_RESET_FIELD; - + if (i == numblock - 1) flags = CSETBLOCK_HALT + CSETBLOCK_RESET_FIELD; + + if (gen == 2) + /* generation 1b magic card */ + flags |= CSETBLOCK_MAGIC_1B; + if (mfCGetBlock(i, buf, flags)) { PrintAndLog("Cant get block: %d", i); break; } - + if (mfEmlSetMem(buf, i, 1)) { PrintAndLog("Cant set emul block: %d", i); return 3; @@ -1971,27 +2543,35 @@ int CmdHF14AMfCSave(const char *Cmd) { } return 0; } else { - len = strlen(Cmd); + param_getstr(Cmd, 0, filename, sizeof(filename)); + + len = strlen(filename); if (len > FILE_PATH_SIZE - 5) len = FILE_PATH_SIZE - 5; - - if (len < 1) { + + ctmp = param_getchar(Cmd, 0); + if (len < 1 || (ctmp == '4')) { // get filename - if (mfCGetBlock(0, buf, CSETBLOCK_SINGLE_OPER)) { + + flags = CSETBLOCK_SINGLE_OPER; + if (gen == 2) + /* generation 1b magic card */ + flags |= CSETBLOCK_MAGIC_1B; + if (mfCGetBlock(0, buf, flags)) { PrintAndLog("Cant get block: %d", 0); len = sprintf(fnameptr, "dump"); fnameptr += len; } else { for (j = 0; j < 7; j++, fnameptr += 2) - sprintf(fnameptr, "%02x", buf[j]); + sprintf(fnameptr, "%02x", buf[j]); } } else { - memcpy(filename, Cmd, len); + //memcpy(filename, Cmd, len); fnameptr += len; } - sprintf(fnameptr, ".eml"); - + sprintf(fnameptr, ".eml"); + // open file f = fopen(filename, "w+"); @@ -2002,22 +2582,25 @@ int CmdHF14AMfCSave(const char *Cmd) { // put hex flags = CSETBLOCK_INIT_FIELD + CSETBLOCK_WUPC; - for (i = 0; i < 16 * 4; i++) { + for (i = 0; i < numblock; i++) { if (i == 1) flags = 0; - if (i == 16 * 4 - 1) flags = CSETBLOCK_HALT + CSETBLOCK_RESET_FIELD; - + if (i == numblock - 1) flags = CSETBLOCK_HALT + CSETBLOCK_RESET_FIELD; + + if (gen == 2) + /* generation 1b magic card */ + flags |= CSETBLOCK_MAGIC_1B; if (mfCGetBlock(i, buf, flags)) { PrintAndLog("Cant get block: %d", i); break; } for (j = 0; j < 16; j++) - fprintf(f, "%02x", buf[j]); + fprintf(f, "%02x", buf[j]); fprintf(f,"\n"); } fclose(f); - + PrintAndLog("Saved to file: %s", filename); - + return 0; } } @@ -2030,9 +2613,10 @@ int CmdHF14AMfSniff(const char *Cmd){ //bool wantSaveToEml = 0; TODO bool wantSaveToEmlFile = 0; - //var + //var int res = 0; int len = 0; + int parlen = 0; int blockLen = 0; int pckNum = 0; int num = 0; @@ -2044,7 +2628,8 @@ int CmdHF14AMfSniff(const char *Cmd){ uint8_t *buf = NULL; uint16_t bufsize = 0; uint8_t *bufPtr = NULL; - + uint8_t parity[16]; + char ctmp = param_getchar(Cmd, 0); if ( ctmp == 'h' || ctmp == 'H' ) { PrintAndLog("It continuously gets data from the field and saves it to: log, emulator, emulator file."); @@ -2056,8 +2641,8 @@ int CmdHF14AMfSniff(const char *Cmd){ PrintAndLog("Usage: hf mf sniff [l][d][e][f]"); PrintAndLog(" sample: hf mf sniff l d e"); return 0; - } - + } + for (int i = 0; i < 4; i++) { ctmp = param_getchar(Cmd, i); if (ctmp == 'l' || ctmp == 'L') wantLogToFile = true; @@ -2065,7 +2650,7 @@ int CmdHF14AMfSniff(const char *Cmd){ //if (ctmp == 'e' || ctmp == 'E') wantSaveToEml = true; TODO if (ctmp == 'f' || ctmp == 'F') wantSaveToEmlFile = true; } - + printf("-------------------------------------------------------------------------\n"); printf("Executing command. \n"); printf("Press the key on the proxmark3 device to abort both proxmark3 and client.\n"); @@ -2085,25 +2670,24 @@ int CmdHF14AMfSniff(const char *Cmd){ printf("\naborted via keyboard!\n"); break; } - + UsbCommand resp; - if (WaitForResponseTimeout(CMD_ACK,&resp,2000)) { + if (WaitForResponseTimeoutW(CMD_ACK, &resp, 2000, false)) { res = resp.arg[0] & 0xff; uint16_t traceLen = resp.arg[1]; len = resp.arg[2]; - if (res == 0) { // we are done - free(buf); - return 0; + if (res == 0) { // we are done + break; } - if (res == 1) { // there is (more) data to be transferred - if (pckNum == 0) { // first packet, (re)allocate necessary buffer + if (res == 1) { // there is (more) data to be transferred + if (pckNum == 0) { // first packet, (re)allocate necessary buffer if (traceLen > bufsize || buf == NULL) { uint8_t *p; - if (buf == NULL) { // not yet allocated + if (buf == NULL) { // not yet allocated p = malloc(traceLen); - } else { // need more memory + } else { // need more memory p = realloc(buf, traceLen); } if (p == NULL) { @@ -2122,13 +2706,13 @@ int CmdHF14AMfSniff(const char *Cmd){ pckNum++; } - if (res == 2) { // received all data, start displaying + if (res == 2) { // received all data, start displaying blockLen = bufPtr - buf; bufPtr = buf; printf(">\n"); PrintAndLog("received trace len: %d packages: %d", blockLen, pckNum); while (bufPtr - buf < blockLen) { - bufPtr += 6; // skip (void) timing information + bufPtr += 6; // skip (void) timing information len = *((uint16_t *)bufPtr); if(len & 0x8000) { isTag = true; @@ -2136,33 +2720,41 @@ int CmdHF14AMfSniff(const char *Cmd){ } else { isTag = false; } + parlen = (len - 1) / 8 + 1; bufPtr += 2; if ((len == 14) && (bufPtr[0] == 0xff) && (bufPtr[1] == 0xff) && (bufPtr[12] == 0xff) && (bufPtr[13] == 0xff)) { memcpy(uid, bufPtr + 2, 7); memcpy(atqa, bufPtr + 2 + 7, 2); uid_len = (atqa[0] & 0xC0) == 0x40 ? 7 : 4; sak = bufPtr[11]; - PrintAndLog("tag select uid:%s atqa:0x%02x%02x sak:0x%02x", + PrintAndLog("tag select uid:%s atqa:0x%02x%02x sak:0x%02x", sprint_hex(uid + (7 - uid_len), uid_len), - atqa[1], - atqa[0], + atqa[1], + atqa[0], sak); if (wantLogToFile || wantDecrypt) { FillFileNameByUID(logHexFileName, uid + (7 - uid_len), ".log", uid_len); AddLogCurrentDT(logHexFileName); - } - if (wantDecrypt) + } + if (wantDecrypt) mfTraceInit(uid, atqa, sak, wantSaveToEmlFile); } else { - PrintAndLog("%s(%d):%s", isTag ? "TAG":"RDR", num, sprint_hex(bufPtr, len)); - if (wantLogToFile) + oddparitybuf(bufPtr, len, parity); + PrintAndLog("%s(%d):%s [%s] c[%s]%c", + isTag ? "TAG":"RDR", + num, + sprint_hex(bufPtr, len), + printBitsPar(bufPtr + len, len), + printBitsPar(parity, len), + memcmp(bufPtr + len, parity, len / 8 + 1) ? '!' : ' '); + if (wantLogToFile) AddLogHex(logHexFileName, isTag ? "TAG: ":"RDR: ", bufPtr, len); - if (wantDecrypt) - mfTraceDecode(bufPtr, len, wantSaveToEmlFile); - num++; + if (wantDecrypt) + mfTraceDecode(bufPtr, len, bufPtr[len], wantSaveToEmlFile); + num++; } bufPtr += len; - bufPtr += ((len-1)/8+1); // ignore parity + bufPtr += parlen; // ignore parity } pckNum = 0; } @@ -2170,59 +2762,375 @@ int CmdHF14AMfSniff(const char *Cmd){ } // while (true) free(buf); + + msleep(300); // wait for exiting arm side. + PrintAndLog("Done."); return 0; } //needs nt, ar, at, Data to decrypt int CmdDecryptTraceCmds(const char *Cmd){ uint8_t data[50]; - int len = 0; - param_gethex_ex(Cmd,3,data,&len); - return tryDecryptWord(param_get32ex(Cmd,0,0,16),param_get32ex(Cmd,1,0,16),param_get32ex(Cmd,2,0,16),data,len/2); + int len = 100; + param_gethex_ex(Cmd, 3, data, &len); + return tryDecryptWord(param_get32ex(Cmd, 0, 0, 16), param_get32ex(Cmd, 1, 0, 16), param_get32ex(Cmd, 2, 0, 16), data, len/2); } -static command_t CommandTable[] = -{ - {"help", CmdHelp, 1, "This help"}, - {"dbg", CmdHF14AMfDbg, 0, "Set default debug mode"}, - {"rdbl", CmdHF14AMfRdBl, 0, "Read MIFARE classic block"}, - {"rdsc", CmdHF14AMfRdSc, 0, "Read MIFARE classic sector"}, - {"dump", CmdHF14AMfDump, 0, "Dump MIFARE classic tag to binary file"}, - {"restore", CmdHF14AMfRestore, 0, "Restore MIFARE classic binary file to BLANK tag"}, - {"wrbl", CmdHF14AMfWrBl, 0, "Write MIFARE classic block"}, - {"chk", CmdHF14AMfChk, 0, "Test block keys"}, - {"mifare", CmdHF14AMifare, 0, "Read parity error messages."}, - {"nested", CmdHF14AMfNested, 0, "Test nested authentication"}, - {"sniff", CmdHF14AMfSniff, 0, "Sniff card-reader communication"}, - {"sim", CmdHF14AMf1kSim, 0, "Simulate MIFARE card"}, - {"eclr", CmdHF14AMfEClear, 0, "Clear simulator memory block"}, - {"eget", CmdHF14AMfEGet, 0, "Get simulator memory block"}, - {"eset", CmdHF14AMfESet, 0, "Set simulator memory block"}, - {"eload", CmdHF14AMfELoad, 0, "Load from file emul dump"}, - {"esave", CmdHF14AMfESave, 0, "Save to file emul dump"}, - {"ecfill", CmdHF14AMfECFill, 0, "Fill simulator memory with help of keys from simulator"}, - {"ekeyprn", CmdHF14AMfEKeyPrn, 0, "Print keys from simulator memory"}, - {"csetuid", CmdHF14AMfCSetUID, 0, "Set UID for magic Chinese card"}, - {"csetblk", CmdHF14AMfCSetBlk, 0, "Write block - Magic Chinese card"}, - {"cgetblk", CmdHF14AMfCGetBlk, 0, "Read block - Magic Chinese card"}, - {"cgetsc", CmdHF14AMfCGetSc, 0, "Read sector - Magic Chinese card"}, - {"cload", CmdHF14AMfCLoad, 0, "Load dump into magic Chinese card"}, - {"csave", CmdHF14AMfCSave, 0, "Save dump from magic Chinese card into file or emulator"}, - {"decrypt", CmdDecryptTraceCmds,1, "[nt] [ar_enc] [at_enc] [data] - to decrypt snoop or trace"}, - {NULL, NULL, 0, NULL} +int CmdHF14AMfAuth4(const char *cmd) { + uint8_t keyn[20] = {0}; + int keynlen = 0; + uint8_t key[16] = {0}; + int keylen = 0; + + CLIParserInit("hf mf auth4", + "Executes AES authentication command in ISO14443-4", + "Usage:\n\thf mf auth4 4000 000102030405060708090a0b0c0d0e0f -> executes authentication\n" + "\thf 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(cmd, argtable, true); + + CLIGetHexWithReturn(1, keyn, &keynlen); + CLIGetHexWithReturn(2, key, &keylen); + CLIParserFree(); + + if (keynlen != 2) { + PrintAndLog("ERROR: must be 2 bytes long instead of: %d", keynlen); + return 1; + } + + if (keylen != 16) { + PrintAndLog("ERROR: must be 16 bytes long instead of: %d", keylen); + return 1; + } + + return MifareAuth4(NULL, keyn, key, true, false, true); +} + +// https://www.nxp.com/docs/en/application-note/AN10787.pdf +int CmdHF14AMfMAD(const char *cmd) { + + CLIParserInit("hf mf mad", + "Checks and prints Mifare Application Directory (MAD)", + "Usage:\n\thf mf mad -> shows MAD if exists\n" + "\thf mf mad -a 03e1 -k ffffffffffff -b -> shows NDEF data if exists. read card with custom key and key B\n"); + + void *argtable[] = { + arg_param_begin, + arg_lit0("vV", "verbose", "show technical data"), + arg_str0("aA", "aid", "print all sectors with aid", NULL), + arg_str0("kK", "key", "key for printing sectors", NULL), + arg_lit0("bB", "keyb", "use key B for access printing sectors (by default: key A)"), + arg_param_end + }; + CLIExecWithReturn(cmd, argtable, true); + bool verbose = arg_get_lit(1); + uint8_t aid[2] = {0}; + int aidlen; + CLIGetHexWithReturn(2, aid, &aidlen); + uint8_t key[6] = {0}; + int keylen; + CLIGetHexWithReturn(3, key, &keylen); + bool keyB = arg_get_lit(4); + + CLIParserFree(); + + if (aidlen != 2 && keylen > 0) { + PrintAndLogEx(WARNING, "do not need a key without aid."); + } + + uint8_t sector0[16 * 4] = {0}; + uint8_t sector10[16 * 4] = {0}; + if (mfReadSector(MF_MAD1_SECTOR, MF_KEY_A, (uint8_t *)g_mifare_mad_key, sector0)) { + PrintAndLogEx(ERR, "read sector 0 error. card don't have MAD or don't have MAD on default keys."); + return 2; + } + + if (verbose) { + for (int i = 0; i < 4; i ++) + PrintAndLogEx(NORMAL, "[%d] %s", i, sprint_hex(§or0[i * 16], 16)); + } + + bool haveMAD2 = false; + MAD1DecodeAndPrint(sector0, verbose, &haveMAD2); + + if (haveMAD2) { + if (mfReadSector(MF_MAD2_SECTOR, MF_KEY_A, (uint8_t *)g_mifare_mad_key, sector10)) { + PrintAndLogEx(ERR, "read sector 0x10 error. card don't have MAD or don't have MAD on default keys."); + return 2; + } + + MAD2DecodeAndPrint(sector10, verbose); + } + + if (aidlen == 2) { + uint16_t aaid = (aid[0] << 8) + aid[1]; + PrintAndLogEx(NORMAL, "\n-------------- AID 0x%04x ---------------", aaid); + + uint16_t mad[7 + 8 + 8 + 8 + 8] = {0}; + size_t madlen = 0; + if (MADDecode(sector0, sector10, mad, &madlen)) { + PrintAndLogEx(ERR, "can't decode mad."); + return 10; + } + + uint8_t akey[6] = {0}; + memcpy(akey, g_mifare_ndef_key, 6); + if (keylen == 6) { + memcpy(akey, key, 6); + } + + 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, "read sector %d error.", i + 1); + return 2; + } + + for (int j = 0; j < (verbose ? 4 : 3); j ++) + PrintAndLogEx(NORMAL, " [%03d] %s", (i + 1) * 4 + j, sprint_hex(&vsector[j * 16], 16)); + } + } + } + + return 0; +} + +int CmdHFMFNDEF(const char *cmd) { + + CLIParserInit("hf mf ndef", + "Prints NFC Data Exchange Format (NDEF)", + "Usage:\n\thf mf ndef -> shows NDEF data\n" + "\thf mf ndef -a 03e1 -k ffffffffffff -b -> shows NDEF data with custom AID, key and with key B\n"); + + void *argtable[] = { + arg_param_begin, + arg_litn("vV", "verbose", 0, 2, "show technical data"), + arg_str0("aA", "aid", "replace default aid for NDEF", NULL), + arg_str0("kK", "key", "replace default key for NDEF", NULL), + arg_lit0("bB", "keyb", "use key B for access sectors (by default: key A)"), + arg_param_end + }; + CLIExecWithReturn(cmd, argtable, true); + + bool verbose = arg_get_lit(1); + bool verbose2 = arg_get_lit(1) > 1; + uint8_t aid[2] = {0}; + int aidlen; + CLIGetHexWithReturn(2, aid, &aidlen); + uint8_t key[6] = {0}; + int keylen; + CLIGetHexWithReturn(3, key, &keylen); + bool keyB = arg_get_lit(4); + + CLIParserFree(); + + uint16_t ndefAID = 0x03e1; + 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; + + PrintAndLogEx(NORMAL, ""); + + if (mfReadSector(MF_MAD1_SECTOR, MF_KEY_A, (uint8_t *)g_mifare_mad_key, sector0)) { + PrintAndLogEx(ERR, "read sector 0 error. card don't have MAD or don't have MAD on default keys."); + return 2; + } + + bool haveMAD2 = false; + int res = MADCheck(sector0, NULL, verbose, &haveMAD2); + if (res) { + PrintAndLogEx(ERR, "MAD error %d.", res); + return res; + } + + if (haveMAD2) { + if (mfReadSector(MF_MAD2_SECTOR, MF_KEY_A, (uint8_t *)g_mifare_mad_key, sector10)) { + PrintAndLogEx(ERR, "read sector 0x10 error. card don't have MAD or don't have MAD on default keys."); + return 2; + } + } + + uint16_t mad[7 + 8 + 8 + 8 + 8] = {0}; + size_t madlen = 0; + if (MADDecode(sector0, (haveMAD2 ? sector10 : NULL), mad, &madlen)) { + PrintAndLogEx(ERR, "can't decode mad."); + return 10; + } + + printf("data reading:"); + 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, "read sector %d error.", i + 1); + return 2; + } + + memcpy(&data[datalen], vsector, 16 * 3); + datalen += 16 * 3; + + printf("."); + } + } + printf(" OK\n"); + + if (!datalen) { + PrintAndLogEx(ERR, "no NDEF data."); + return 11; + } + + if (verbose2) { + PrintAndLogEx(NORMAL, "NDEF data:"); + dump_buffer(data, datalen, stdout, 1); + } + + NDEFDecodeAndPrint(data, datalen, verbose); + + return 0; +} + +int CmdHFMFPersonalize(const char *cmd) { + + CLIParserInit("hf mf personalize", + "Personalize the UID of a Mifare Classic EV1 card. This is only possible if it is a 7Byte UID card and if it is not already personalized.", + "Usage:\n\thf mf personalize UIDF0 -> double size UID according to ISO/IEC14443-3\n" + "\thf mf personalize UIDF1 -> double size UID according to ISO/IEC14443-3, optional usage of selection process shortcut\n" + "\thf mf personalize UIDF2 -> single size random ID according to ISO/IEC14443-3\n" + "\thf mf personalize UIDF3 -> single size NUID according to ISO/IEC14443-3\n" + "\thf mf personalize -t B -k B0B1B2B3B4B5 UIDF3 -> use key B = 0xB0B1B2B3B4B5 instead of default key A\n"); + + void *argtable[] = { + arg_param_begin, + arg_str0("tT", "keytype", "", "key type (A or B) to authenticate sector 0 (default: A)"), + arg_str0("kK", "key", "", "key to authenticate sector 0 (default: FFFFFFFFFFFF)"), + arg_str1(NULL, NULL, "", "Personalization Option"), + arg_param_end + }; + CLIExecWithReturn(cmd, argtable, true); + + char keytypestr[2] = "A"; + uint8_t keytype = 0x00; + int keytypestr_len; + int res = CLIParamStrToBuf(arg_get_str(1), (uint8_t*)keytypestr, 1, &keytypestr_len); + if (res || (keytypestr[0] != 'a' && keytypestr[0] != 'A' && keytypestr[0] != 'b' && keytypestr[0] != 'B')) { + PrintAndLog("ERROR: not a valid key type. Key type must be A or B"); + CLIParserFree(); + return 1; + } + if (keytypestr[0] == 'B' || keytypestr[0] == 'b') { + keytype = 0x01; + } + + uint8_t key[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + int key_len; + res = CLIParamHexToBuf(arg_get_str(2), key, 6, &key_len); + if (res || (!res && key_len > 0 && key_len != 6)) { + PrintAndLog("ERROR: not a valid key. Key must be 12 hex digits"); + CLIParserFree(); + return 1; + } + + char pers_optionstr[6]; + int opt_len; + uint8_t pers_option; + res = CLIParamStrToBuf(arg_get_str(3), (uint8_t*)pers_optionstr, 5, &opt_len); + if (res || (!res && opt_len > 0 && opt_len != 5) + || (strncmp(pers_optionstr, "UIDF0", 5) && strncmp(pers_optionstr, "UIDF1", 5) && strncmp(pers_optionstr, "UIDF2", 5) && strncmp(pers_optionstr, "UIDF3", 5))) { + PrintAndLog("ERROR: invalid personalization option. Must be one of UIDF0, UIDF1, UIDF2, or UIDF3"); + CLIParserFree(); + return 1; + } + if (!strncmp(pers_optionstr, "UIDF0", 5)) { + pers_option = MIFARE_EV1_UIDF0; + } else if (!strncmp(pers_optionstr, "UIDF1", 5)) { + pers_option = MIFARE_EV1_UIDF1; + } else if (!strncmp(pers_optionstr, "UIDF2", 5)) { + pers_option = MIFARE_EV1_UIDF2; + } else { + pers_option = MIFARE_EV1_UIDF3; + } + + CLIParserFree(); + + UsbCommand c = {CMD_MIFARE_PERSONALIZE_UID, {keytype, pers_option, 0}}; + memcpy(c.d.asBytes, key, 6); + SendCommand(&c); + + UsbCommand resp; + if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { + uint8_t isOK = resp.arg[0] & 0xff; + PrintAndLog("Personalization %s", isOK ? "FAILED" : "SUCCEEDED"); + } else { + PrintAndLog("Command execute timeout"); + } + + return 0; +} + + +static command_t CommandTable[] = { + {"help", CmdHelp, 1, "This help"}, + {"dbg", CmdHF14AMfDbg, 0, "Set default debug mode"}, + {"rdbl", CmdHF14AMfRdBl, 0, "Read MIFARE classic block"}, + {"rdsc", CmdHF14AMfRdSc, 0, "Read MIFARE classic sector"}, + {"dump", CmdHF14AMfDump, 0, "Dump MIFARE classic tag to binary file"}, + {"restore", CmdHF14AMfRestore, 0, "Restore MIFARE classic binary file to BLANK tag"}, + {"wrbl", CmdHF14AMfWrBl, 0, "Write MIFARE classic block"}, + {"auth4", CmdHF14AMfAuth4, 0, "ISO14443-4 AES authentication"}, + {"chk", CmdHF14AMfChk, 0, "Test block keys"}, + {"mifare", CmdHF14AMifare, 0, "Read parity error messages."}, + {"hardnested", CmdHF14AMfNestedHard, 0, "Nested attack for hardened Mifare cards"}, + {"nested", CmdHF14AMfNested, 0, "Test nested authentication"}, + {"sniff", CmdHF14AMfSniff, 0, "Sniff card-reader communication"}, + {"sim", CmdHF14AMfSim, 0, "Simulate MIFARE card"}, + {"eclr", CmdHF14AMfEClear, 0, "Clear simulator memory"}, + {"eget", CmdHF14AMfEGet, 0, "Get simulator memory block"}, + {"eset", CmdHF14AMfESet, 0, "Set simulator memory block"}, + {"eload", CmdHF14AMfELoad, 0, "Load from file emul dump"}, + {"esave", CmdHF14AMfESave, 0, "Save to file emul dump"}, + {"ecfill", CmdHF14AMfECFill, 0, "Fill simulator memory with help of keys from simulator"}, + {"ekeyprn", CmdHF14AMfEKeyPrn, 0, "Print keys from simulator memory"}, + {"cwipe", CmdHF14AMfCWipe, 0, "Wipe magic Chinese card"}, + {"csetuid", CmdHF14AMfCSetUID, 0, "Set UID for magic Chinese card"}, + {"csetblk", CmdHF14AMfCSetBlk, 0, "Write block - Magic Chinese card"}, + {"cgetblk", CmdHF14AMfCGetBlk, 0, "Read block - Magic Chinese card"}, + {"cgetsc", CmdHF14AMfCGetSc, 0, "Read sector - Magic Chinese card"}, + {"cload", CmdHF14AMfCLoad, 0, "Load dump into magic Chinese card"}, + {"csave", CmdHF14AMfCSave, 0, "Save dump from magic Chinese card into file or emulator"}, + {"decrypt", CmdDecryptTraceCmds, 1, "[nt] [ar_enc] [at_enc] [data] - to decrypt snoop or trace"}, + {"mad", CmdHF14AMfMAD, 0, "Checks and prints MAD"}, + {"ndef", CmdHFMFNDEF, 0, "Prints NDEF records from card"}, + {"personalize", CmdHFMFPersonalize, 0, "Personalize UID (Mifare Classic EV1 only)"}, + {NULL, NULL, 0, NULL} }; -int CmdHFMF(const char *Cmd) -{ - // flush - WaitForResponseTimeout(CMD_ACK,NULL,100); - CmdsParse(CommandTable, Cmd); - return 0; +int CmdHFMF(const char *Cmd) { + (void)WaitForResponseTimeout(CMD_ACK,NULL,100); + CmdsParse(CommandTable, Cmd); + return 0; } -int CmdHelp(const char *Cmd) -{ - CmdsHelp(CommandTable); - return 0; + +int CmdHelp(const char *Cmd) { + CmdsHelp(CommandTable); + return 0; }