X-Git-Url: http://cvs.zerfleddert.de/cgi-bin/gitweb.cgi/proxmark3-svn/blobdiff_plain/3ded0f97d32f91766a5815b6c74ca7c6ebade8bf..cfa9c98d573c5a0d35c6ce86501fc6fd57f91b81:/client/cmdhfmf.c diff --git a/client/cmdhfmf.c b/client/cmdhfmf.c index 5ebf8144..c38e276e 100644 --- a/client/cmdhfmf.c +++ b/client/cmdhfmf.c @@ -15,18 +15,27 @@ #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" + +#define NESTED_SECTOR_RETRY 10 // how often we try mfested() until we give up static int CmdHelp(const char *Cmd); @@ -57,7 +66,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 "); @@ -105,7 +114,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) { @@ -136,12 +145,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; @@ -155,7 +178,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 "); @@ -196,6 +219,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"); @@ -223,14 +255,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) { @@ -248,8 +280,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; @@ -262,37 +293,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; @@ -541,7 +582,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 } @@ -566,7 +607,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; @@ -607,14 +648,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); @@ -641,7 +682,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); @@ -657,7 +698,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); } @@ -681,10 +722,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); @@ -713,7 +754,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) { @@ -724,17 +765,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)); @@ -770,7 +811,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); } @@ -781,7 +822,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|"); @@ -862,6 +903,13 @@ int CmdHF14AMfNestedHard(const char *Cmd) 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"); @@ -880,15 +928,20 @@ int CmdHF14AMfNestedHard(const char *Cmd) 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); @@ -922,20 +975,55 @@ int CmdHF14AMfNestedHard(const char *Cmd) 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 and/or s"); + 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', @@ -966,10 +1054,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 (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"); @@ -978,26 +1066,27 @@ int CmdHF14AMfChk(const char *Cmd) return 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; - 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 - bool param3InUse = false; - - 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 + uint8_t btimeout14a = 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); @@ -1010,53 +1099,53 @@ int CmdHF14AMfChk(const char *Cmd) if (param_getchar(Cmd, 0)=='*') { SectorsCnt = ParamCardSizeSectors(param_getchar(Cmd + 1, 0)); - } - else + } 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 + } 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 ?"); - free(keyBlock); - return 1; - }; - - // transfer to emulator & create dump file - ctmp = param_getchar(Cmd, 2); - if (ctmp == 't' || ctmp == 'T') transferToEml = 1; - if (ctmp == 'd' || ctmp == 'D') createDumpFile = 1; - - param3InUse = transferToEml | createDumpFile; - - timeout14a = 500; // fast by default - // double parameters - ts, ds - int clen = param_getlength(Cmd, 2); - if (clen == 2 || clen == 3){ - param_getstr(Cmd, 2, ctmp3, sizeof(ctmp3)); - ctmp = ctmp3[1]; + 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; + }; } - //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); + + 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; } + 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==0?'A':keyType==1?'B':'?', 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)) { if ( stKeyBlock - keycnt < 2) { @@ -1069,8 +1158,8 @@ 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 @@ -1080,23 +1169,29 @@ int CmdHF14AMfChk(const char *Cmd) 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( 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); + 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"); @@ -1117,7 +1212,6 @@ int CmdHF14AMfChk(const char *Cmd) PrintAndLog("File: %s: not found or locked.", filename); free(keyBlock); return 1; - } } } @@ -1127,13 +1221,16 @@ int CmdHF14AMfChk(const char *Cmd) 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 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; @@ -1143,14 +1240,18 @@ int CmdHF14AMfChk(const char *Cmd) printf("\n"); bool foundAKey = false; - uint32_t max_keys = keycnt > USB_CMD_DATA_SIZE / 6 ? USB_CMD_DATA_SIZE / 6 : keycnt; - if (SectorsCnt) { + 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; - 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, clearTraceLog, size, &keyBlock[6 * c], e_sector); // timeout is (ms * 106)/10 or us*0.0106 + clearTraceLog = false; if (res != 1) { if (!res) { @@ -1164,18 +1265,26 @@ int CmdHF14AMfChk(const char *Cmd) PrintAndLog("Command execute timeout"); } } - } else { + } else { int keyAB = keyType; do { - for (uint32_t c = 0; c < keycnt; c+=max_keys) { + 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); + clearTraceLog = false; if (res != 1) { - if (!res) { - PrintAndLog("Found valid key:[%d:%c]%012" PRIx64, blockNo, (keyAB & 0x01)?'B':'A', key64); + 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"); @@ -1183,25 +1292,33 @@ int CmdHF14AMfChk(const char *Cmd) } } while(--keyAB > 0); } - + // print result if (foundAKey) { - if (SectorsCnt) { - PrintAndLog(""); - PrintAndLog("|---|----------------|---|----------------|---|"); - PrintAndLog("|sec|key A |res|key B |res|"); - PrintAndLog("|---|----------------|---|----------------|---|"); - for (i = 0; i < SectorsCnt; i++) { - PrintAndLog("|%03d| %012" PRIx64 " | %d | %012" PRIx64 " | %d |", i, - e_sector[i].Key[0], e_sector[i].foundKey[0], e_sector[i].Key[1], e_sector[i].foundKey[1]); + 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); } - PrintAndLog("|---|----------------|---|----------------|---|"); } + PrintAndLog("|---|----------------|----------------|"); } else { PrintAndLog(""); PrintAndLog("No valid keys found."); - } - + } + if (transferToEml) { uint8_t block[16]; for (uint16_t sectorNo = 0; sectorNo < SectorsCnt; sectorNo++) { @@ -1210,15 +1327,17 @@ int CmdHF14AMfChk(const char *Cmd) for (uint16_t t = 0; t < 2; t++) { 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) { PrintAndLog("Could not create file dumpkeys.bin"); @@ -1236,16 +1355,17 @@ int CmdHF14AMfChk(const char *Cmd) 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 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; @@ -1254,7 +1374,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)); @@ -1317,7 +1437,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)); @@ -1346,11 +1466,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)"); @@ -1359,21 +1480,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; @@ -1384,9 +1504,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; @@ -1410,7 +1542,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; @@ -1418,7 +1550,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': @@ -1428,12 +1560,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; @@ -1450,7 +1584,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) { @@ -1477,7 +1611,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: @@ -1490,18 +1623,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... " } @@ -1518,22 +1655,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... " } @@ -1632,10 +1774,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); } @@ -1673,7 +1812,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; @@ -1854,37 +1993,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("|---|----------------|----------------|"); @@ -1901,9 +2046,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}; @@ -1915,7 +2086,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; @@ -1924,12 +2095,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; @@ -1983,7 +2154,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"); @@ -1994,9 +2165,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; @@ -2016,7 +2187,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'); @@ -2026,10 +2197,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) { @@ -2056,7 +2227,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); @@ -2122,9 +2293,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 */ @@ -2174,9 +2345,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 */ @@ -2235,6 +2406,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; } @@ -2285,6 +2470,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; } @@ -2420,6 +2618,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; @@ -2431,6 +2630,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' ) { @@ -2474,23 +2674,22 @@ int CmdHF14AMfSniff(const char *Cmd){ } 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) { @@ -2509,13 +2708,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; @@ -2523,6 +2722,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); @@ -2541,15 +2741,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; } @@ -2557,15 +2764,249 @@ 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); +} + +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[] = @@ -2575,15 +3016,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"}, @@ -2598,16 +3040,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)