X-Git-Url: http://cvs.zerfleddert.de/cgi-bin/gitweb.cgi/proxmark3-svn/blobdiff_plain/8ec06f5ef3c5c63a420b8292dd3ea5d0a5d6974e..2378bb24c3d4ce21d71b4ab5739c58a9979b8b69:/client/cmdhfmf.c?ds=sidebyside diff --git a/client/cmdhfmf.c b/client/cmdhfmf.c index b653cf30..9284d14c 100644 --- a/client/cmdhfmf.c +++ b/client/cmdhfmf.c @@ -15,19 +15,26 @@ #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" +#include "mifare/mfkey.h" #include "hardnested/hardnested_bf_core.h" +#include "cliparser/cliparser.h" +#include "cmdhf14a.h" +#include "mifare/mifare4.h" +#include "mifare/mad.h" +#include "mifare/ndef.h" +#include "emv/dump.h" -#define NESTED_SECTOR_RETRY 10 // how often we try mfested() until we give up +#define NESTED_SECTOR_RETRY 10 // how often we try mfested() until we give up static int CmdHelp(const char *Cmd); @@ -58,7 +65,7 @@ int CmdHF14AMfWrBl(const char *Cmd) 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 "); @@ -106,7 +113,7 @@ int CmdHF14AMfRdBl(const char *Cmd) uint8_t keyType = 0; uint8_t key[6] = {0, 0, 0, 0, 0, 0}; - char cmdp = 0x00; + char cmdp = 0x00; if (strlen(Cmd)<3) { @@ -137,12 +144,26 @@ int CmdHF14AMfRdBl(const char *Cmd) 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; @@ -156,7 +177,7 @@ 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 "); @@ -197,6 +218,15 @@ 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"); @@ -224,14 +254,14 @@ uint8_t NumBlocksPerSector(uint8_t sectorNo) } static int ParamCardSizeSectors(const char c) { - int numBlocks = 16; + int numSectors = 16; switch (c) { - case '0' : numBlocks = 5; break; - case '2' : numBlocks = 32; break; - case '4' : numBlocks = 40; break; - default: numBlocks = 16; + case '0' : numSectors = 5; break; + case '2' : numSectors = 32; break; + case '4' : numSectors = 40; break; + default: numSectors = 16; } - return numBlocks; + return numSectors; } static int ParamCardSizeBlocks(const char c) { @@ -249,8 +279,7 @@ 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; @@ -263,37 +292,38 @@ int CmdHF14AMfDump(const char *Cmd) char cmdp = param_getchar(Cmd, 0); numSectors = ParamCardSizeSectors(cmdp); - if (strlen(Cmd) > 1 || cmdp == 'h' || cmdp == 'H') { - PrintAndLog("Usage: hf mf dump [card memory]"); + 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 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; @@ -542,7 +581,7 @@ static void parseParamTDS(const char *Cmd, const uint8_t indx, bool *paramT, boo // slow and very slow if (ctmp3[0] == 's' || ctmp3[0] == 'S' || ctmp3[1] == 's' || ctmp3[1] == 'S') { *timeout = 11; // slow - + if (!paramS1 && (ctmp3[1] == 's' || ctmp3[1] == 'S')) { *timeout = 53; // very slow } @@ -567,7 +606,7 @@ int CmdHF14AMfNested(const char *Cmd) uint64_t key64 = 0; // timeout in units. (ms * 106)/10 or us*0.0106 uint8_t btimeout14a = MF_CHKKEYS_DEFTIMEOUT; // fast by default - + bool autosearchKey = false; bool transferToEml = false; @@ -608,14 +647,14 @@ int CmdHF14AMfNested(const char *Cmd) } else { SectorsCnt = ParamCardSizeSectors(cmdp); } - + // . number or autosearch key (*) if (param_getchar(Cmd, 1) == '*') { autosearchKey = true; parseParamTDS(Cmd, 2, &transferToEml, &createDumpFile, &btimeout14a); - PrintAndLog("--nested. sectors:%2d, block no:*, eml:%c, dmp=%c checktimeout=%d us", + PrintAndLog("--nested. sectors:%2d, block no:*, eml:%c, dmp=%c checktimeout=%d us", SectorsCnt, transferToEml?'y':'n', createDumpFile?'y':'n', ((int)btimeout14a * 10000) / 106); } else { blockNo = param_get8(Cmd, 1); @@ -642,7 +681,7 @@ int CmdHF14AMfNested(const char *Cmd) } // one sector nested - if (cmdp == 'o') { + if (cmdp == 'o') { trgBlockNo = param_get8(Cmd, 4); ctmp = param_getchar(Cmd, 5); @@ -658,7 +697,7 @@ int CmdHF14AMfNested(const char *Cmd) parseParamTDS(Cmd, 4, &transferToEml, &createDumpFile, &btimeout14a); } - PrintAndLog("--nested. sectors:%2d, block no:%3d, key type:%c, eml:%c, dmp=%c checktimeout=%d us", + 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', ((int)btimeout14a * 10000) / 106); } @@ -682,10 +721,10 @@ int CmdHF14AMfNested(const char *Cmd) // 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); @@ -714,7 +753,7 @@ int CmdHF14AMfNested(const char *Cmd) PrintAndLog("Testing known keys. Sector count=%d", SectorsCnt); mfCheckKeysSec(SectorsCnt, 2, btimeout14a, true, MifareDefaultKeysSize, keyBlock, e_sector); - + // get known key from array bool keyFound = false; if (autosearchKey) { @@ -725,17 +764,17 @@ int CmdHF14AMfNested(const char *Cmd) 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)); @@ -771,7 +810,7 @@ int CmdHF14AMfNested(const char *Cmd) 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, btimeout14a, true, 1, keyBlock, e_sector); } @@ -782,7 +821,7 @@ int CmdHF14AMfNested(const char *Cmd) // 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|"); @@ -951,7 +990,7 @@ int CmdHF14AMfNestedHard(const char *Cmd) i++; } } - + SetSIMDInstr(SIMD_AUTO); if (iindx > 0) { while ((ctmp = param_getchar(Cmd, iindx))) { @@ -981,7 +1020,7 @@ int CmdHF14AMfNestedHard(const char *Cmd) } } 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 ", @@ -1014,10 +1053,10 @@ int CmdHF14AMfChk(const char *Cmd) 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("s - slow execute. timeout 1ms"); - PrintAndLog("ss- very slow execute. timeout 5ms"); + PrintAndLog("d - write keys to binary file\n"); + 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"); @@ -1033,20 +1072,20 @@ int CmdHF14AMfChk(const char *Cmd) uint16_t stKeyBlock = 20; int i, res; - int keycnt = 0; - char ctmp = 0x00; + int keycnt = 0; + char ctmp = 0x00; int clen = 0; - char ctmp3[3] = {0x00}; uint8_t blockNo = 0; uint8_t SectorsCnt = 0; uint8_t keyType = 0; uint64_t key64 = 0; - uint32_t timeout14a = 0; // timeout in us + // timeout in units. (ms * 106)/10 or us*0.0106 + uint8_t btimeout14a = MF_CHKKEYS_DEFTIMEOUT; // fast by default bool param3InUse = false; - int transferToEml = 0; - int createDumpFile = 0; - + bool transferToEml = 0; + bool createDumpFile = 0; + sector_t *e_sector = NULL; keyBlock = calloc(stKeyBlock, 6); @@ -1083,32 +1122,12 @@ int CmdHF14AMfChk(const char *Cmd) }; } - // transfer to emulator & create dump file - ctmp = param_getchar(Cmd, 2); - clen = param_getlength(Cmd, 2); - if (clen == 1 && (ctmp == 't' || ctmp == 'T')) transferToEml = 1; - if (clen == 1 && (ctmp == 'd' || ctmp == 'D')) createDumpFile = 1; - - param3InUse = transferToEml | createDumpFile; - - timeout14a = 500; // fast by default - // double parameters - ts, ds - clen = param_getlength(Cmd, 2); - if (clen == 2 || clen == 3){ - param_getstr(Cmd, 2, ctmp3, sizeof(ctmp3)); - ctmp = ctmp3[1]; - } - //parse - if (ctmp == 's' || ctmp == 'S') { - timeout14a = 1000; // slow - if (!param3InUse && clen == 2 && (ctmp3[1] == 's' || ctmp3[1] == 'S')) { - timeout14a = 5000; // very slow - } - if (param3InUse && clen == 3 && (ctmp3[2] == 's' || ctmp3[2] == 'S')) { - timeout14a = 5000; // very slow - } - param3InUse = true; - } + parseParamTDS(Cmd, 2, &transferToEml, &createDumpFile, &btimeout14a); + + param3InUse = transferToEml | createDumpFile | (btimeout14a != MF_CHKKEYS_DEFTIMEOUT); + + PrintAndLog("--chk keys. 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', ((int)btimeout14a * 10000) / 106); for (i = param3InUse; param_getchar(Cmd, 2 + i); i++) { if (!param_gethex(Cmd, 2 + i, keyBlock + 6 * keycnt, 12)) { @@ -1123,7 +1142,7 @@ int CmdHF14AMfChk(const char *Cmd) } 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)[3], (keyBlock + 6*keycnt)[4], (keyBlock + 6*keycnt)[5], 6); keycnt++; } else { // May be a dic file @@ -1140,7 +1159,7 @@ int CmdHF14AMfChk(const char *Cmd) while (fgetc(f) != '\n' && !feof(f)) ; //goto next line - if( buf[0]=='#' ) continue; //The line start with # is comment, skip + if( buf[0]=='#' ) continue; //The line start with # is comment, skip if (!isxdigit((unsigned char)buf[0])){ PrintAndLog("File content error. '%s' must include 12 HEX symbols",buf); @@ -1181,12 +1200,15 @@ int CmdHF14AMfChk(const char *Cmd) 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)[3], (keyBlock + 6*keycnt)[4], (keyBlock + 6*keycnt)[5], 6); } // initialize storage for found keys e_sector = calloc(SectorsCnt, sizeof(sector_t)); - if (e_sector == NULL) return 1; + if (e_sector == NULL) { + free(keyBlock); + return 1; + } for (uint8_t keyAB = 0; keyAB < 2; keyAB++) { for (uint16_t sectorNo = 0; sectorNo < SectorsCnt; sectorNo++) { e_sector[sectorNo].Key[keyAB] = 0xffffffffffff; @@ -1203,7 +1225,7 @@ int CmdHF14AMfChk(const char *Cmd) for (uint32_t c = 0; c < keycnt; c += max_keys) { uint32_t size = keycnt-c > max_keys ? max_keys : keycnt-c; - res = mfCheckKeysSec(SectorsCnt, keyType, timeout14a * 1.06 / 100, true, size, &keyBlock[6 * c], e_sector); // timeout is (ms * 106)/10 or us*0.0106 + res = mfCheckKeysSec(SectorsCnt, keyType, btimeout14a, true, size, &keyBlock[6 * c], e_sector); // timeout is (ms * 106)/10 or us*0.0106 if (res != 1) { if (!res) { @@ -1223,7 +1245,7 @@ int CmdHF14AMfChk(const char *Cmd) for (uint32_t c = 0; c < keycnt; c+=max_keys) { uint32_t size = keycnt-c > max_keys ? max_keys : keycnt-c; - res = mfCheckKeys(blockNo, keyAB & 0x01, true, size, &keyBlock[6 * c], &key64); + res = mfCheckKeys(blockNo, keyAB & 0x01, true, size, &keyBlock[6 * c], &key64); if (res != 1) { if (!res) { @@ -1236,7 +1258,7 @@ int CmdHF14AMfChk(const char *Cmd) } } while(--keyAB > 0); } - + // print result if (foundAKey) { if (SectorsCnt) { @@ -1253,8 +1275,8 @@ int CmdHF14AMfChk(const char *Cmd) } else { PrintAndLog(""); PrintAndLog("No valid keys found."); - } - + } + if (transferToEml) { uint8_t block[16]; for (uint16_t sectorNo = 0; sectorNo < SectorsCnt; sectorNo++) { @@ -1298,7 +1320,7 @@ int CmdHF14AMfChk(const char *Cmd) void readerAttack(nonces_t ar_resp[], bool setEmulatorMem, bool doStandardAttack) { #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) + // cannot be more than 7 or it will overrun c.d.asBytes(512) uint64_t key = 0; typedef struct { uint64_t keyA; @@ -1307,7 +1329,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)); @@ -1370,7 +1392,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)); @@ -1399,11 +1421,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)"); @@ -1412,21 +1435,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; @@ -1437,9 +1459,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; @@ -1463,7 +1497,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; @@ -1471,7 +1505,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': @@ -1483,10 +1517,9 @@ int CmdHF14AMf1kSim(const char *Cmd) { case 'U': param_gethex_ex(Cmd, cmdp+1, uid, &uidlen); 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: return usage_hf14_mf1ksim(); + default: return usage_hf14_mfsim(); } cmdp += 2; break; @@ -1503,7 +1536,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) { @@ -1530,7 +1563,6 @@ 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: @@ -1543,18 +1575,22 @@ int CmdHF14AMf1kSim(const char *Cmd) { 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); + 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,0}}; + 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... " } @@ -1571,22 +1607,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... " } @@ -1685,10 +1726,7 @@ int CmdHF14AMfESet(const char *Cmd) } // 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); } @@ -1726,7 +1764,7 @@ int CmdHF14AMfELoad(const char *Cmd) } } - len = param_getstr(Cmd,nameParamNo,filename,sizeof(filename)); + len = param_getstr(Cmd, nameParamNo, filename, sizeof(filename)); if (len > FILE_PATH_SIZE - 5) len = FILE_PATH_SIZE - 5; @@ -1907,37 +1945,43 @@ int CmdHF14AMfECFill(const char *Cmd) 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; } + 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; } - 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; + 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++; } PrintAndLog("|---|----------------|----------------|"); @@ -1954,9 +1998,35 @@ int CmdHF14AMfEKeyPrn(const char *Cmd) } 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+6, 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 uid[8] = {0x00}; @@ -1968,7 +2038,7 @@ int CmdHF14AMfCSetUID(const char *Cmd) uint8_t needHelp = 0; char cmdp = 1; - + if (param_getchar(Cmd, 0) && param_gethex(Cmd, 0, uid, 8)) { PrintAndLog("UID must include 8 HEX symbols"); return 1; @@ -1977,12 +2047,12 @@ int CmdHF14AMfCSetUID(const char *Cmd) if (param_getlength(Cmd, 1) > 1 && param_getlength(Cmd, 2) > 1) { atqaPresent = 1; cmdp = 3; - + 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; @@ -2036,7 +2106,7 @@ int CmdHF14AMfCWipe(const char *Cmd) 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"); @@ -2047,9 +2117,9 @@ int CmdHF14AMfCWipe(const char *Cmd) } gen = mfCIdentify(); - if ((gen != 1) && (gen != 2)) + if ((gen != 1) && (gen != 2)) return 1; - + numBlocks = ParamCardSizeBlocks(param_getchar(Cmd, 0)); char cmdp = 0; @@ -2069,7 +2139,7 @@ int CmdHF14AMfCWipe(const char *Cmd) cmdp++; } - if (!wipeCard && !fillCard) + if (!wipeCard && !fillCard) wipeCard = true; PrintAndLog("--blocks count:%2d wipe:%c fill:%c", numBlocks, (wipeCard)?'y':'n', (fillCard)?'y':'n'); @@ -2079,10 +2149,10 @@ int CmdHF14AMfCWipe(const char *Cmd) if (wipeCard) { PrintAndLog("WARNING: can't wipe magic card 1b generation"); } - res = mfCWipe(numBlocks, true, false, fillCard); + res = mfCWipe(numBlocks, true, false, fillCard); } else { /* generation 1a magic card by default */ - res = mfCWipe(numBlocks, false, wipeCard, fillCard); + res = mfCWipe(numBlocks, false, wipeCard, fillCard); } if (res) { @@ -2109,7 +2179,7 @@ int CmdHF14AMfCSetBlk(const char *Cmd) } gen = mfCIdentify(); - if ((gen != 1) && (gen != 2)) + if ((gen != 1) && (gen != 2)) return 1; blockNo = param_get8(Cmd, 0); @@ -2175,9 +2245,9 @@ int CmdHF14AMfCLoad(const char *Cmd) 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 == numblock - 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 */ @@ -2227,9 +2297,9 @@ 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 == numblock - 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 */ @@ -2288,6 +2358,20 @@ int CmdHF14AMfCGetBlk(const char *Cmd) { } 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; } @@ -2338,6 +2422,19 @@ int CmdHF14AMfCGetSc(const char *Cmd) { } 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; } @@ -2473,6 +2570,7 @@ int CmdHF14AMfSniff(const char *Cmd){ //var int res = 0; int len = 0; + int parlen = 0; int blockLen = 0; int pckNum = 0; int num = 0; @@ -2484,6 +2582,7 @@ 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' ) { @@ -2532,17 +2631,17 @@ int CmdHF14AMfSniff(const char *Cmd){ uint16_t traceLen = resp.arg[1]; len = resp.arg[2]; - if (res == 0) { // we are done + 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) { @@ -2561,13 +2660,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; @@ -2575,6 +2674,7 @@ 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); @@ -2593,15 +2693,22 @@ int CmdHF14AMfSniff(const char *Cmd){ if (wantDecrypt) mfTraceInit(uid, atqa, sak, wantSaveToEmlFile); } else { - PrintAndLog("%s(%d):%s", isTag ? "TAG":"RDR", num, sprint_hex(bufPtr, len)); + 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); + mfTraceDecode(bufPtr, len, bufPtr[len], wantSaveToEmlFile); num++; } bufPtr += len; - bufPtr += ((len-1)/8+1); // ignore parity + bufPtr += parlen; // ignore parity } pckNum = 0; } @@ -2609,7 +2716,7 @@ int CmdHF14AMfSniff(const char *Cmd){ } // while (true) free(buf); - + msleep(300); // wait for exiting arm side. PrintAndLog("Done."); return 0; @@ -2623,6 +2730,237 @@ int CmdDecryptTraceCmds(const char *Cmd){ return tryDecryptWord(param_get32ex(Cmd,0,0,16),param_get32ex(Cmd,1,0,16),param_get32ex(Cmd,2,0,16),data,len/2); } +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; +} + static command_t CommandTable[] = { {"help", CmdHelp, 1, "This help"}, @@ -2630,15 +2968,16 @@ static command_t CommandTable[] = {"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"}, + {"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", CmdHF14AMf1kSim, 0, "Simulate MIFARE card"}, - {"eclr", CmdHF14AMfEClear, 0, "Clear simulator memory block"}, + {"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"}, @@ -2653,16 +2992,16 @@ static command_t CommandTable[] = {"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"}, {NULL, NULL, 0, NULL} }; int CmdHFMF(const char *Cmd) { - // flush - WaitForResponseTimeout(CMD_ACK,NULL,100); - - CmdsParse(CommandTable, Cmd); - return 0; + (void)WaitForResponseTimeout(CMD_ACK,NULL,100); + CmdsParse(CommandTable, Cmd); + return 0; } int CmdHelp(const char *Cmd)