From 275d9e61c245e65fe09ccb5c49f09dfe505a642d Mon Sep 17 00:00:00 2001 From: Oleg Moiseenko Date: Sun, 15 Oct 2017 22:19:34 +0300 Subject: [PATCH] Check keys in hf mf nested and hf mf chk (#414) Improve hf mf chk and hf mf nested * hf mf chk. added interrupt of procedure by usb * extract mifare default keys into separate module * arm side multisector `hf mf chk` * hf mf nested. change key search procedure * hf mf nested. added key check after we have found a key. * small fix hf list f * hf mf chk. add timeout (arm side) and some tweaks. --- CHANGELOG.md | 3 + armsrc/apps.h | 2 +- armsrc/iso14443a.h | 1 + armsrc/mifarecmd.c | 87 +++++--------- armsrc/mifareutil.c | 122 +++++++++++++++++++ armsrc/mifareutil.h | 9 ++ client/cmdhf.c | 2 +- client/cmdhfmf.c | 265 ++++++++++++++++++----------------------- client/cmdhfmf.h | 2 + client/cmdmain.c | 9 +- client/cmdmain.h | 1 + client/mifaredefault.h | 40 +++++++ client/mifarehost.c | 33 ++++- client/mifarehost.h | 13 +- 14 files changed, 373 insertions(+), 216 deletions(-) create mode 100644 client/mifaredefault.h diff --git a/CHANGELOG.md b/CHANGELOG.md index 842c8bef..45fe1392 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,8 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac - Deleted wipe functionality from `hf mf csetuid` (Merlok) - Changed `hf mf nested` logic (Merlok) - Added `hf mf nested` mode: autosearch keys for attack (from well known keys) (Merlok) +- `hf mf nested` Check keys after they have found (Merlok) +- `hf mf chk` Move main cycle to arm (Merlok) ### Fixed @@ -23,6 +25,7 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac - Added `hf mf cwipe` command. It wipes "magic Chinese" card. For 1a generation it uses card's "wipe" command. For gen1a and gen1b it uses a write command. (Merlok) - Added to `hf mf nested` source key check before attack (Merlok) - Added to `hf mf nested` after attack it checks all found keys on non-open sectors (Merlok) +- `hf mf chk` Added setings to set iso14443a operations timeout. default timeout set to 500us (Merlok) ## [3.0.1][2017-06-08] diff --git a/armsrc/apps.h b/armsrc/apps.h index 1ce3215f..b0070148 100644 --- a/armsrc/apps.h +++ b/armsrc/apps.h @@ -127,7 +127,7 @@ void MifareWriteBlock(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain) void MifareUWriteBlock(uint8_t arg0, uint8_t arg1, uint8_t *datain); void MifareNested(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain); void MifareAcquireEncryptedNonces(uint32_t arg0, uint32_t arg1, uint32_t flags, uint8_t *datain); -void MifareChkKeys(uint16_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain); +void MifareChkKeys(uint16_t arg0, uint16_t arg1, uint8_t arg2, uint8_t *datain); void Mifare1ksim(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain); void MifareSetDbgLvl(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain); void MifareEMemClr(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain); diff --git a/armsrc/iso14443a.h b/armsrc/iso14443a.h index 10e50e0f..9977a658 100644 --- a/armsrc/iso14443a.h +++ b/armsrc/iso14443a.h @@ -29,4 +29,5 @@ extern void iso14443a_setup(uint8_t fpga_minor_mode); extern int iso14_apdu(uint8_t *cmd, uint16_t cmd_len, void *data); extern int iso14443a_select_card(uint8_t *uid_ptr, iso14a_card_select_t *resp_data, uint32_t *cuid_ptr, bool anticollision, uint8_t num_cascades, bool no_rats); extern void iso14a_set_trigger(bool enable); +extern void iso14a_set_timeout(uint32_t timeout); #endif /* __ISO14443A_H */ diff --git a/armsrc/mifarecmd.c b/armsrc/mifarecmd.c index a3f0d374..edafe0a3 100644 --- a/armsrc/mifarecmd.c +++ b/armsrc/mifarecmd.c @@ -20,10 +20,6 @@ #include "parity.h" #include "crc.h" -#define AUTHENTICATION_TIMEOUT 848 // card times out 1ms after wrong authentication (according to NXP documentation) -#define PRE_AUTHENTICATION_LEADTIME 400 // some (non standard) cards need a pause after select before they are ready for first authentication - - // the block number for the ISO14443-4 PCB static uint8_t pcb_blocknum = 0; // Deselect card by sending a s-block. the crc is precalced for speed @@ -961,24 +957,14 @@ void MifareNested(uint32_t arg0, uint32_t arg1, uint32_t calibrate, uint8_t *dat // MIFARE check keys. key count up to 85. // //----------------------------------------------------------------------------- -void MifareChkKeys(uint16_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain) +void MifareChkKeys(uint16_t arg0, uint16_t arg1, uint8_t arg2, uint8_t *datain) { uint8_t blockNo = arg0 & 0xff; uint8_t keyType = (arg0 >> 8) & 0xff; - bool clearTrace = arg1; + bool clearTrace = arg1 & 0x01; + bool multisectorCheck = arg1 & 0x02; + uint8_t set14aTimeout = (arg1 >> 8) & 0xff; uint8_t keyCount = arg2; - uint64_t ui64Key = 0; - - bool have_uid = false; - uint8_t cascade_levels = 0; - uint32_t timeout = 0; - int i; - byte_t isOK = 0; - uint8_t uid[10]; - uint32_t cuid; - struct Crypto1State mpcs = {0, 0}; - struct Crypto1State *pcs; - pcs = &mpcs; // clear debug level int OLD_MF_DBGLEVEL = MF_DBGLEVEL; @@ -992,53 +978,34 @@ void MifareChkKeys(uint16_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain) if (clearTrace) clear_trace(); set_tracing(true); - for (i = 0; i < keyCount; i++) { -// if(mifare_classic_halt(pcs, cuid)) { -// if (MF_DBGLEVEL >= 1) Dbprintf("ChkKeys: Halt error"); -// } + if (set14aTimeout){ + iso14a_set_timeout(set14aTimeout * 10); // timeout: ms = x/106 35-minimum, 50-OK 106-recommended 500-safe + } + + if (multisectorCheck) { + TKeyIndex keyIndex = {{0}}; + uint8_t sectorCnt = blockNo; + int res = MifareMultisectorChk(datain, keyCount, sectorCnt, keyType, OLD_MF_DBGLEVEL, &keyIndex); - // Iceman: use piwi's faster nonce collecting part in hardnested. - if (!have_uid) { // need a full select cycle to get the uid first - iso14a_card_select_t card_info; - if(!iso14443a_select_card(uid, &card_info, &cuid, true, 0, true)) { - if (OLD_MF_DBGLEVEL >= 1) Dbprintf("ChkKeys: Can't select card"); - --i; // try same key once again - continue; - } - switch (card_info.uidlen) { - case 4 : cascade_levels = 1; break; - case 7 : cascade_levels = 2; break; - case 10: cascade_levels = 3; break; - default: break; - } - have_uid = true; - } else { // no need for anticollision. We can directly select the card - if(!iso14443a_select_card(uid, NULL, NULL, false, cascade_levels, true)) { - if (OLD_MF_DBGLEVEL >= 1) Dbprintf("ChkKeys: Can't select card (UID)"); - --i; // try same key once again - continue; - } + LED_B_ON(); + if (res >= 0) { + cmd_send(CMD_ACK, 1, 0, 0, keyIndex, 80); + } else { + cmd_send(CMD_ACK, 0, 0, 0, NULL, 0); } - - ui64Key = bytes_to_num(datain + i * 6, 6); - if(mifare_classic_auth(pcs, cuid, blockNo, keyType, ui64Key, AUTH_FIRST)) { - uint8_t dummy_answer = 0; - ReaderTransmit(&dummy_answer, 1, NULL); - timeout = GetCountSspClk() + AUTHENTICATION_TIMEOUT; - - // wait for the card to become ready again - while(GetCountSspClk() < timeout); - continue; + LED_B_OFF(); + } else { + int res = MifareChkBlockKeys(datain, keyCount, blockNo, keyType, OLD_MF_DBGLEVEL); + + LED_B_ON(); + if (res > 0) { + cmd_send(CMD_ACK, 1, 0, 0, datain + (res - 1) * 6, 6); + } else { + cmd_send(CMD_ACK, 0, 0, 0, NULL, 0); } - - isOK = 1; - break; + LED_B_OFF(); } - LED_B_ON(); - cmd_send(CMD_ACK,isOK,0,0,datain + i * 6,6); - LED_B_OFF(); - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LEDsoff(); diff --git a/armsrc/mifareutil.c b/armsrc/mifareutil.c index fdead5ee..97f7b3d1 100644 --- a/armsrc/mifareutil.c +++ b/armsrc/mifareutil.c @@ -764,3 +764,125 @@ int mifare_desfire_des_auth2(uint32_t uid, uint8_t *key, uint8_t *blockData){ } return 1; } + +//----------------------------------------------------------------------------- +// MIFARE check keys +// +//----------------------------------------------------------------------------- +// one key check +int MifareChkBlockKey(uint8_t *uid, uint32_t *cuid, uint8_t *cascade_levels, uint64_t ui64Key, uint8_t blockNo, uint8_t keyType, uint8_t debugLevel) { + + struct Crypto1State mpcs = {0, 0}; + struct Crypto1State *pcs; + pcs = &mpcs; + + // Iceman: use piwi's faster nonce collecting part in hardnested. + if (*cascade_levels == 0) { // need a full select cycle to get the uid first + iso14a_card_select_t card_info; + if(!iso14443a_select_card(uid, &card_info, cuid, true, 0, true)) { + if (debugLevel >= 1) Dbprintf("ChkKeys: Can't select card"); + return 1; + } + switch (card_info.uidlen) { + case 4 : *cascade_levels = 1; break; + case 7 : *cascade_levels = 2; break; + case 10: *cascade_levels = 3; break; + default: break; + } + } else { // no need for anticollision. We can directly select the card + if(!iso14443a_select_card(uid, NULL, NULL, false, *cascade_levels, true)) { + if (debugLevel >= 1) Dbprintf("ChkKeys: Can't select card (UID) lvl=%d", *cascade_levels); + return 1; + } + } + + if(mifare_classic_auth(pcs, *cuid, blockNo, keyType, ui64Key, AUTH_FIRST)) { +// SpinDelayUs(AUTHENTICATION_TIMEOUT); // it not needs because mifare_classic_auth have timeout from iso14a_set_timeout() + return 2; + } else { +/* // let it be here. it like halt command, but maybe it will work in some strange cases + uint8_t dummy_answer = 0; + ReaderTransmit(&dummy_answer, 1, NULL); + int timeout = GetCountSspClk() + AUTHENTICATION_TIMEOUT; + // wait for the card to become ready again + while(GetCountSspClk() < timeout) {}; +*/ + // it needs after success authentication + mifare_classic_halt(pcs, *cuid); + } + + return 0; +} + +// multi key check +int MifareChkBlockKeys(uint8_t *keys, uint8_t keyCount, uint8_t blockNo, uint8_t keyType, uint8_t debugLevel) { + uint8_t uid[10]; + uint32_t cuid = 0; + uint8_t cascade_levels = 0; + uint64_t ui64Key = 0; + + int retryCount = 0; + for (uint8_t i = 0; i < keyCount; i++) { + + // Allow button press / usb cmd to interrupt device + if (BUTTON_PRESS() && !usb_poll_validate_length()) { + Dbprintf("ChkKeys: Cancel operation. Exit..."); + return -2; + } + + ui64Key = bytes_to_num(keys + i * 6, 6); + int res = MifareChkBlockKey(uid, &cuid, &cascade_levels, ui64Key, blockNo, keyType, debugLevel); + + // can't select + if (res == 1) { + retryCount++; + if (retryCount >= 5) { + Dbprintf("ChkKeys: block=%d key=%d. Can't select. Exit...", blockNo, keyType); + return -1; + } + --i; // try the same key once again + + SpinDelay(20); +// Dbprintf("ChkKeys: block=%d key=%d. Try the same key once again...", blockNo, keyType); + continue; + } + + // can't authenticate + if (res == 2) { + retryCount = 0; + continue; // can't auth. wrong key. + } + + return i + 1; + } + + return 0; +} + +// multisector multikey check +int MifareMultisectorChk(uint8_t *keys, uint8_t keyCount, uint8_t SectorCount, uint8_t keyType, uint8_t debugLevel, TKeyIndex *keyIndex) { + int res = 0; + +// int clk = GetCountSspClk(); + + for(int sc = 0; sc < SectorCount; sc++){ + WDT_HIT(); + + int keyAB = keyType; + do { + res = MifareChkBlockKeys(keys, keyCount, FirstBlockOfSector(sc), keyAB & 0x01, debugLevel); + if (res < 0){ + return res; + } + if (res > 0){ + (*keyIndex)[keyAB & 0x01][sc] = res; + } + } while(--keyAB > 0); + } + +// Dbprintf("%d %d", GetCountSspClk() - clk, (GetCountSspClk() - clk)/(SectorCount*keyCount*(keyType==2?2:1))); + + return 0; +} + + diff --git a/armsrc/mifareutil.h b/armsrc/mifareutil.h index 468c5cce..8ffd5e89 100644 --- a/armsrc/mifareutil.h +++ b/armsrc/mifareutil.h @@ -13,6 +13,7 @@ #define __MIFAREUTIL_H #include "crapto1/crapto1.h" +#include "usb_cdc.h" // mifare authentication #define CRYPT_NONE 0 @@ -20,6 +21,8 @@ #define CRYPT_REQUEST 2 #define AUTH_FIRST 0 #define AUTH_NESTED 2 +#define AUTHENTICATION_TIMEOUT 848 // card times out 1ms after wrong authentication (according to NXP documentation) +#define PRE_AUTHENTICATION_LEADTIME 400 // some (non standard) cards need a pause after select before they are ready for first authentication // mifare 4bit card answers #define CARD_ACK 0x0A // 1010 - ACK @@ -99,4 +102,10 @@ int emlGetValBl(uint32_t *blReg, uint8_t *blBlock, int blockNum); int emlSetValBl(uint32_t blReg, uint8_t blBlock, int blockNum); int emlCheckValBl(int blockNum); +// mifare check keys +typedef uint8_t TKeyIndex[2][40]; +int MifareChkBlockKey(uint8_t *uid, uint32_t *cuid, uint8_t *cascade_levels, uint64_t ui64Key, uint8_t blockNo, uint8_t keyType, uint8_t debugLevel); +int MifareChkBlockKeys(uint8_t *keys, uint8_t keyCount, uint8_t blockNo, uint8_t keyType, uint8_t debugLevel); +int MifareMultisectorChk(uint8_t *keys, uint8_t keyCount, uint8_t SectorCount, uint8_t keyType, uint8_t debugLevel, TKeyIndex *keyIndex); + #endif diff --git a/client/cmdhf.c b/client/cmdhf.c index eb2ba9b2..6aa5ae4e 100644 --- a/client/cmdhf.c +++ b/client/cmdhf.c @@ -546,7 +546,7 @@ uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *trace, ui if (showWaitCycles && !isResponse && next_record_is_response(tracepos, trace)) { uint32_t next_timestamp = *((uint32_t *)(trace + tracepos)); - PrintAndLog(" %9d | %9d | %s | fdt (Frame Delay Time): %d", + PrintAndLog(" %10d | %10d | %s | fdt (Frame Delay Time): %d", (EndOfTransmissionTimestamp - first_timestamp), (next_timestamp - first_timestamp), " ", diff --git a/client/cmdhfmf.c b/client/cmdhfmf.c index 295f22b7..12fb0b78 100644 --- a/client/cmdhfmf.c +++ b/client/cmdhfmf.c @@ -524,13 +524,6 @@ int CmdHF14AMfRestore(const char *Cmd) return 0; } - -typedef struct { - uint64_t Key[2]; - int foundKey[2]; -} sector_t; - - # define NESTED_KEY_COUNT 15 int CmdHF14AMfNested(const char *Cmd) { @@ -686,35 +679,12 @@ int CmdHF14AMfNested(const char *Cmd) if (e_sector == NULL) return 1; //test current key and additional standard keys first - memcpy(keyBlock, key, 6); - num_to_bytes(0xffffffffffff, 6, (uint8_t*)(keyBlock + 1 * 6)); - num_to_bytes(0x000000000000, 6, (uint8_t*)(keyBlock + 2 * 6)); - num_to_bytes(0xa0a1a2a3a4a5, 6, (uint8_t*)(keyBlock + 3 * 6)); - num_to_bytes(0xb0b1b2b3b4b5, 6, (uint8_t*)(keyBlock + 4 * 6)); - num_to_bytes(0xaabbccddeeff, 6, (uint8_t*)(keyBlock + 5 * 6)); - num_to_bytes(0x4d3a99c351dd, 6, (uint8_t*)(keyBlock + 6 * 6)); - num_to_bytes(0x1a982c7e459a, 6, (uint8_t*)(keyBlock + 7 * 6)); - num_to_bytes(0xd3f7d3f7d3f7, 6, (uint8_t*)(keyBlock + 8 * 6)); - num_to_bytes(0x714c5c886e97, 6, (uint8_t*)(keyBlock + 9 * 6)); - num_to_bytes(0x587ee5f9350f, 6, (uint8_t*)(keyBlock + 10 * 6)); - num_to_bytes(0xa0478cc39091, 6, (uint8_t*)(keyBlock + 11 * 6)); - num_to_bytes(0x533cb6c723f6, 6, (uint8_t*)(keyBlock + 12 * 6)); - num_to_bytes(0x8fd0a4f256e9, 6, (uint8_t*)(keyBlock + 13 * 6)); - num_to_bytes(0x1a2b3c4d5e6f, 6, (uint8_t*)(keyBlock + 14 * 6)); + for (int defaultKeyCounter = 0; defaultKeyCounter < MifareDefaultKeysSize; defaultKeyCounter++){ + num_to_bytes(MifareDefaultKeys[defaultKeyCounter], 6, (uint8_t*)(keyBlock + defaultKeyCounter * 6)); + } PrintAndLog("Testing known keys. Sector count=%d", SectorsCnt); - for (i = 0; i < SectorsCnt; i++) { - for (j = 0; j < 2; j++) { - if (e_sector[i].foundKey[j]) continue; - - res = mfCheckKeys(FirstBlockOfSector(i), j, true, NESTED_KEY_COUNT, keyBlock, &key64); - - if (!res) { - e_sector[i].Key[j] = key64; - e_sector[i].foundKey[j] = 1; - } - } - } + mfCheckKeysSec(SectorsCnt, 2, MF_CHKKEYS_DEFTIMEOUT, true, NESTED_KEY_COUNT, keyBlock, e_sector); // get known key from array bool keyFound = false; @@ -772,6 +742,9 @@ 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, MF_CHKKEYS_DEFTIMEOUT, true, 1, keyBlock, e_sector); } } } @@ -781,66 +754,6 @@ int CmdHF14AMfNested(const char *Cmd) 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); - // check if we have unrecognized keys - bool notFoundKeys = false; - for (i = 0; i < SectorsCnt; i++) { - for (j = 0; j < 2; j++) { - if (!e_sector[i].foundKey[j]) { - notFoundKeys = true; - break; - } - } - if (notFoundKeys) break; - } - - if (notFoundKeys) { - PrintAndLog("-----------------------------------------------\n"); - PrintAndLog("We have unrecognized keys. Trying to check if we have this keys on key buffer..."); - - // fill keyBlock with known keys - int cnt = 0; - for (i = 0; i < SectorsCnt; i++) { - for (j = 0; j < 2; j++) { - if (e_sector[i].foundKey[j]) { - // try to insert key to keyBlock - if (cnt < NESTED_KEY_COUNT) { - - // search for dublicates - bool dubl = false; - for (int v = 0; v < NESTED_KEY_COUNT; v++) { - if (e_sector[i].Key[j] == bytes_to_num((uint8_t*)(keyBlock + v * 6), 6)) { - dubl = true; - break; - } - } - - // insert - if (!dubl) { - num_to_bytes(e_sector[i].Key[j], 6, (uint8_t*)(keyBlock + cnt * 6)); - cnt++; - } - } - } - } - } - - // try to auth with known keys to not recognized sectors keys - PrintAndLog("Testing keys. Sector count=%d known keys count:%d", SectorsCnt, cnt); - for (i = 0; i < SectorsCnt; i++) { - for (j = 0; j < 2; j++) { - if (e_sector[i].foundKey[j]) continue; - - res = mfCheckKeys(FirstBlockOfSector(i), j, true, cnt, keyBlock, &key64); - - if (!res) { - e_sector[i].Key[j] = key64; - e_sector[i].foundKey[j] = 1; - } - } - } - - } // if (notFoundKeys) - // print result PrintAndLog("|---|----------------|---|----------------|---|"); PrintAndLog("|sec|key A |res|key B |res|"); @@ -1022,14 +935,18 @@ int CmdHF14AMfNestedHard(const char *Cmd) int CmdHF14AMfChk(const char *Cmd) { if (strlen(Cmd)<3) { - PrintAndLog("Usage: hf mf chk |<*card memory> [t|d] [] []"); + PrintAndLog("Usage: hf mf chk |<*card memory> [t|d|s|ss] [] []"); PrintAndLog(" * - all sectors"); PrintAndLog("card memory - 0 - MINI(320 bytes), 1 - 1K, 2 - 2K, 4 - 4K, - 1K"); PrintAndLog("d - write keys to binary file\n"); PrintAndLog("t - write keys to emulator memory"); + PrintAndLog("s - slow execute. timeout 1ms"); + PrintAndLog("ss- very slow execute. timeout 5ms"); PrintAndLog(" sample: hf mf chk 0 A 1234567890ab keys.dic"); PrintAndLog(" hf mf chk *1 ? t"); PrintAndLog(" hf mf chk *1 ? d"); + PrintAndLog(" hf mf chk *1 ? s"); + PrintAndLog(" hf mf chk *1 ? dss"); return 0; } @@ -1042,42 +959,28 @@ int CmdHF14AMfChk(const char *Cmd) int i, res; int keycnt = 0; char ctmp = 0x00; + char ctmp3[3] = {0x00}; uint8_t blockNo = 0; - uint8_t SectorsCnt = 1; + 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; + + sector_t *e_sector = NULL; keyBlock = calloc(stKeyBlock, 6); if (keyBlock == NULL) return 1; - uint64_t defaultKeys[] = - { - 0xffffffffffff, // Default key (first key used by program if no user defined key) - 0x000000000000, // Blank key - 0xa0a1a2a3a4a5, // NFCForum MAD key - 0xb0b1b2b3b4b5, - 0xaabbccddeeff, - 0x4d3a99c351dd, - 0x1a982c7e459a, - 0xd3f7d3f7d3f7, - 0x714c5c886e97, - 0x587ee5f9350f, - 0xa0478cc39091, - 0x533cb6c723f6, - 0x8fd0a4f256e9 - }; - int defaultKeysSize = sizeof(defaultKeys) / sizeof(uint64_t); - - for (int defaultKeyCounter = 0; defaultKeyCounter < defaultKeysSize; defaultKeyCounter++) - { - num_to_bytes(defaultKeys[defaultKeyCounter], 6, (uint8_t*)(keyBlock + defaultKeyCounter * 6)); + int defaultKeysSize = MifareDefaultKeysSize; + for (int defaultKeyCounter = 0; defaultKeyCounter < defaultKeysSize; defaultKeyCounter++){ + num_to_bytes(MifareDefaultKeys[defaultKeyCounter], 6, (uint8_t*)(keyBlock + defaultKeyCounter * 6)); } if (param_getchar(Cmd, 0)=='*') { - blockNo = 3; SectorsCnt = ParamCardSizeSectors(param_getchar(Cmd + 1, 0)); } else @@ -1086,10 +989,10 @@ int CmdHF14AMfChk(const char *Cmd) ctmp = param_getchar(Cmd, 1); switch (ctmp) { case 'a': case 'A': - keyType = !0; + keyType = 0; break; case 'b': case 'B': - keyType = !1; + keyType = 1; break; case '?': keyType = 2; @@ -1100,11 +1003,33 @@ int CmdHF14AMfChk(const char *Cmd) return 1; }; + // transfer to emulator & create dump file ctmp = param_getchar(Cmd, 2); - if (ctmp == 't' || ctmp == 'T') transferToEml = 1; - else if (ctmp == 'd' || ctmp == 'D') createDumpFile = 1; + 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); + 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; + } - for (i = transferToEml || createDumpFile; param_getchar(Cmd, 2 + i); i++) { + for (i = param3InUse; param_getchar(Cmd, 2 + i); i++) { if (!param_gethex(Cmd, 2 + i, keyBlock + 6 * keycnt, 12)) { if ( stKeyBlock - keycnt < 2) { p = realloc(keyBlock, 6*(stKeyBlock+=10)); @@ -1169,6 +1094,7 @@ int CmdHF14AMfChk(const char *Cmd) } } + // fill with default keys if (keycnt == 0) { PrintAndLog("No key specified, trying default keys"); for (;keycnt < defaultKeysSize; keycnt++) @@ -1178,47 +1104,84 @@ int CmdHF14AMfChk(const char *Cmd) } // initialize storage for found keys - bool validKey[2][40]; - uint8_t foundKey[2][40][6]; - for (uint16_t t = 0; t < 2; t++) { + e_sector = calloc(SectorsCnt, sizeof(sector_t)); + if (e_sector == NULL) return 1; + for (uint8_t keyAB = 0; keyAB < 2; keyAB++) { for (uint16_t sectorNo = 0; sectorNo < SectorsCnt; sectorNo++) { - validKey[t][sectorNo] = false; - for (uint16_t i = 0; i < 6; i++) { - foundKey[t][sectorNo][i] = 0xff; - } + e_sector[sectorNo].Key[keyAB] = 0xffffffffffff; + e_sector[sectorNo].foundKey[keyAB] = 0; } } + printf("\n"); + + bool foundAKey = false; + uint32_t max_keys = keycnt > USB_CMD_DATA_SIZE / 6 ? USB_CMD_DATA_SIZE / 6 : keycnt; + if (SectorsCnt) { + PrintAndLog("To cancel this operation press the button on the proxmark..."); + printf("--"); + for (uint32_t c = 0; c < keycnt; c += max_keys) { - for ( int t = !keyType; t < 2; keyType==2?(t++):(t=2) ) { - int b=blockNo; - for (int i = 0; i < SectorsCnt; ++i) { - PrintAndLog("--sector:%2d, block:%3d, key type:%C, key count:%2d ", i, b, t?'B':'A', keycnt); - uint32_t max_keys = keycnt>USB_CMD_DATA_SIZE/6?USB_CMD_DATA_SIZE/6:keycnt; + 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 + + if (res != 1) { + if (!res) { + printf("o"); + foundAKey = true; + } else { + printf("."); + } + } else { + printf("\n"); + PrintAndLog("Command execute timeout"); + } + } + } else { + int keyAB = keyType; + do { for (uint32_t c = 0; c < keycnt; c+=max_keys) { - uint32_t size = keycnt-c>max_keys?max_keys:keycnt-c; - res = mfCheckKeys(b, t, true, size, &keyBlock[6*c], &key64); + + uint32_t size = keycnt-c > max_keys ? max_keys : keycnt-c; + res = mfCheckKeys(blockNo, keyAB & 0x01, true, size, &keyBlock[6 * c], &key64); + if (res != 1) { if (!res) { - PrintAndLog("Found valid key:[%012" PRIx64 "]",key64); - num_to_bytes(key64, 6, foundKey[t][i]); - validKey[t][i] = true; + PrintAndLog("Found valid key:[%d:%c]%012" PRIx64, blockNo, (keyAB & 0x01)?'B':'A', key64); + foundAKey = true; } } else { PrintAndLog("Command execute timeout"); } } - b<127?(b+=4):(b+=16); - } + } 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("|---|----------------|---|----------------|---|"); + } + } else { + PrintAndLog(""); + PrintAndLog("No valid keys found."); + } + if (transferToEml) { uint8_t block[16]; for (uint16_t sectorNo = 0; sectorNo < SectorsCnt; sectorNo++) { - if (validKey[0][sectorNo] || validKey[1][sectorNo]) { + if (e_sector[sectorNo].foundKey[0] || e_sector[sectorNo].foundKey[1]) { mfEmlGetMem(block, FirstBlockOfSector(sectorNo) + NumBlocksPerSector(sectorNo) - 1, 1); for (uint16_t t = 0; t < 2; t++) { - if (validKey[t][sectorNo]) { - memcpy(block + t*10, foundKey[t][sectorNo], 6); + if (e_sector[sectorNo].foundKey[t]) { + num_to_bytes(e_sector[sectorNo].Key[t], 6, block + t * 10); } } mfEmlSetMem(block, FirstBlockOfSector(sectorNo) + NumBlocksPerSector(sectorNo) - 1, 1); @@ -1231,16 +1194,22 @@ int CmdHF14AMfChk(const char *Cmd) FILE *fkeys = fopen("dumpkeys.bin","wb"); if (fkeys == NULL) { PrintAndLog("Could not create file dumpkeys.bin"); + free(e_sector); free(keyBlock); return 1; } - for (uint16_t t = 0; t < 2; t++) { - fwrite(foundKey[t], 1, 6*SectorsCnt, fkeys); + uint8_t mkey[6]; + for (uint8_t t = 0; t < 2; t++) { + for (uint8_t sectorNo = 0; sectorNo < SectorsCnt; sectorNo++) { + num_to_bytes(e_sector[sectorNo].Key[t], 6, mkey); + fwrite(mkey, 1, 6, fkeys); + } } fclose(fkeys); PrintAndLog("Found keys have been dumped to file dumpkeys.bin. 0xffffffffffff has been inserted for unknown keys."); } + free(e_sector); free(keyBlock); PrintAndLog(""); return 0; diff --git a/client/cmdhfmf.h b/client/cmdhfmf.h index 235fd86d..3bd3e95a 100644 --- a/client/cmdhfmf.h +++ b/client/cmdhfmf.h @@ -11,6 +11,8 @@ #ifndef CMDHFMF_H__ #define CMDHFMF_H__ +#include "mifaredefault.h" + extern int CmdHFMF(const char *Cmd); extern int CmdHF14AMfDbg(const char* cmd); diff --git a/client/cmdmain.c b/client/cmdmain.c index f407cee4..db88b3ac 100644 --- a/client/cmdmain.c +++ b/client/cmdmain.c @@ -139,7 +139,7 @@ int getCommand(UsbCommand* response) * @param ms_timeout * @return true if command was returned, otherwise false */ -bool WaitForResponseTimeout(uint32_t cmd, UsbCommand* response, size_t ms_timeout) { +bool WaitForResponseTimeoutW(uint32_t cmd, UsbCommand* response, size_t ms_timeout, bool show_warning) { UsbCommand resp; @@ -155,7 +155,7 @@ bool WaitForResponseTimeout(uint32_t cmd, UsbCommand* response, size_t ms_timeou } } msleep(10); // XXX ugh - if (dm_seconds == 200) { // Two seconds elapsed + if (dm_seconds == 200 && show_warning) { // Two seconds elapsed PrintAndLog("Waiting for a response from the proxmark..."); PrintAndLog("Don't forget to cancel its operation first by pressing on the button"); } @@ -163,9 +163,12 @@ bool WaitForResponseTimeout(uint32_t cmd, UsbCommand* response, size_t ms_timeou return false; } +bool WaitForResponseTimeout(uint32_t cmd, UsbCommand* response, size_t ms_timeout) { + return WaitForResponseTimeoutW(cmd, response, ms_timeout, true); +} bool WaitForResponse(uint32_t cmd, UsbCommand* response) { - return WaitForResponseTimeout(cmd,response,-1); + return WaitForResponseTimeoutW(cmd, response, -1, true); } diff --git a/client/cmdmain.h b/client/cmdmain.h index 0de3f392..d39bc114 100644 --- a/client/cmdmain.h +++ b/client/cmdmain.h @@ -18,6 +18,7 @@ extern void UsbCommandReceived(UsbCommand *UC); extern int CommandReceived(char *Cmd); +extern bool WaitForResponseTimeoutW(uint32_t cmd, UsbCommand* response, size_t ms_timeout, bool show_warning); extern bool WaitForResponseTimeout(uint32_t cmd, UsbCommand* response, size_t ms_timeout); extern bool WaitForResponse(uint32_t cmd, UsbCommand* response); extern void clearCommandBuffer(); diff --git a/client/mifaredefault.h b/client/mifaredefault.h new file mode 100644 index 00000000..78231c86 --- /dev/null +++ b/client/mifaredefault.h @@ -0,0 +1,40 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2017 Merlok +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// Mifare default constants +//----------------------------------------------------------------------------- + +#ifndef MIFAREDEFAULT_H__ +#define MIFAREDEFAULT_H__ + +#include + +#define MifareDefaultKeysSize sizeof(MifareDefaultKeys) / sizeof(uint64_t) + +static const uint64_t MifareDefaultKeys[] = +{ + 0xffffffffffff, // Default key (first key used by program if no user defined key) + 0x000000000000, // Blank key + 0xa0a1a2a3a4a5, // NFCForum MAD key + 0xb0b1b2b3b4b5, + 0xaabbccddeeff, + 0x1a2b3c4d5e6f, + 0x123456789abc, + 0x010203040506, + 0x123456abcdef, + 0xabcdef123456, + 0x4d3a99c351dd, + 0x1a982c7e459a, + 0xd3f7d3f7d3f7, + 0x714c5c886e97, + 0x587ee5f9350f, + 0xa0478cc39091, + 0x533cb6c723f6, + 0x8fd0a4f256e9 +}; + +#endif diff --git a/client/mifarehost.c b/client/mifarehost.c index 629c8feb..a02019a3 100644 --- a/client/mifarehost.c +++ b/client/mifarehost.c @@ -228,17 +228,46 @@ int mfCheckKeys (uint8_t blockNo, uint8_t keyType, bool clear_trace, uint8_t key *key = -1; - UsbCommand c = {CMD_MIFARE_CHKKEYS, {((blockNo & 0xff) | ((keyType&0xff)<<8)), clear_trace, keycnt}}; + UsbCommand c = {CMD_MIFARE_CHKKEYS, {((blockNo & 0xff) | ((keyType & 0xff) << 8)), clear_trace, keycnt}}; memcpy(c.d.asBytes, keyBlock, 6 * keycnt); SendCommand(&c); UsbCommand resp; - if (!WaitForResponseTimeout(CMD_ACK,&resp,3000)) return 1; + if (!WaitForResponseTimeout(CMD_ACK,&resp,3000)) return 1; if ((resp.arg[0] & 0xff) != 0x01) return 2; *key = bytes_to_num(resp.d.asBytes, 6); return 0; } +int mfCheckKeysSec(uint8_t sectorCnt, uint8_t keyType, uint8_t timeout14a, bool clear_trace, uint8_t keycnt, uint8_t * keyBlock, sector_t * e_sector){ + + uint8_t keyPtr = 0; + + if (e_sector == NULL) + return -1; + + UsbCommand c = {CMD_MIFARE_CHKKEYS, {((sectorCnt & 0xff) | ((keyType & 0xff) << 8)), (clear_trace | 0x02)|((timeout14a & 0xff) << 8), keycnt}}; + memcpy(c.d.asBytes, keyBlock, 6 * keycnt); + SendCommand(&c); + + UsbCommand resp; + if (!WaitForResponseTimeoutW(CMD_ACK, &resp, MAX(3000, 1000 + 13 * sectorCnt * keycnt * (keyType == 2 ? 2 : 1)), false)) return 1; // timeout: 13 ms / fail auth + if ((resp.arg[0] & 0xff) != 0x01) return 2; + + bool foundAKey = false; + for(int sec = 0; sec < sectorCnt; sec++){ + for(int keyAB = 0; keyAB < 2; keyAB++){ + keyPtr = *(resp.d.asBytes + keyAB * 40 + sec); + if (keyPtr){ + e_sector[sec].foundKey[keyAB] = true; + e_sector[sec].Key[keyAB] = bytes_to_num(keyBlock + (keyPtr - 1) * 6, 6); + foundAKey = true; + } + } + } + return foundAKey ? 0 : 3; +} + // Compare 16 Bits out of cryptostate int Compare16Bits(const void * a, const void * b) { if ((*(uint64_t*)b & 0x00ff000000ff0000) == (*(uint64_t*)a & 0x00ff000000ff0000)) return 0; diff --git a/client/mifarehost.h b/client/mifarehost.h index 34793a29..118d55cc 100644 --- a/client/mifarehost.h +++ b/client/mifarehost.h @@ -1,4 +1,4 @@ -// Merlok, 2011 +// Merlok, 2011, 2017 // people from mifare@nethemba.com, 2010 // // This code is licensed to you under the terms of the GNU GPL, version 2 or, @@ -15,6 +15,11 @@ #include #include "data.h" +// defaults +// timeout in units. (ms * 106)/10 or us*0.0106 +// 5 == 500us +#define MF_CHKKEYS_DEFTIMEOUT 5 + // mfCSetBlock work flags #define CSETBLOCK_UID 0x01 #define CSETBLOCK_WUPC 0x02 @@ -24,11 +29,17 @@ #define CSETBLOCK_SINGLE_OPER 0x1F #define CSETBLOCK_MAGIC_1B 0x40 +typedef struct { + uint64_t Key[2]; + int foundKey[2]; +} sector_t; + extern char logHexFileName[FILE_PATH_SIZE]; extern int mfDarkside(uint64_t *key); extern int mfnested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo, uint8_t trgKeyType, uint8_t *ResultKeys, bool calibrate); extern int mfCheckKeys (uint8_t blockNo, uint8_t keyType, bool clear_trace, uint8_t keycnt, uint8_t *keyBlock, uint64_t *key); +extern int mfCheckKeysSec(uint8_t sectorCnt, uint8_t keyType, uint8_t timeout14a, bool clear_trace, uint8_t keycnt, uint8_t * keyBlock, sector_t * e_sector); extern int mfEmlGetMem(uint8_t *data, int blockNum, int blocksCount); extern int mfEmlSetMem(uint8_t *data, int blockNum, int blocksCount); -- 2.39.5