From 2ca0ea8cb44d1e8333dba708501f34658ea48e74 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Thu, 5 Oct 2017 16:00:56 +0200 Subject: [PATCH] ADD: 'hf mf fastchk' - new command, improved check keys functionality. It uses a bunch of techniques to get a speedup. Using a dictionary file with 421keys, Current implementation of checkkeys takes 300 sec. This implementation of checkkeys takes 250 sec. I implemented it as a separate command so it will be easier to compare between the old and new checkkeys. Its also doing much on deviceside, which is a step to much funnier standalone modes :)) --- armsrc/appmain.c | 23 ++-- armsrc/apps.h | 1 + armsrc/iso14443a.c | 54 ++++++++ armsrc/iso14443a.h | 1 + armsrc/mifarecmd.c | 312 +++++++++++++++++++++++++++++++++++++++++++- client/cmdhfmf.c | 314 ++++++++++++++++++++++++++++++++++++++++++++- 6 files changed, 693 insertions(+), 12 deletions(-) diff --git a/armsrc/appmain.c b/armsrc/appmain.c index de431bb06..2476f442c 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -26,9 +26,6 @@ #include "LCD.h" #endif -// Global var to determine if device is in standalone mode or not. -static int InStandAloneMode = 0; - //============================================================================= // A buffer where we can queue things up to be sent through the FPGA, for // any purpose (fake tag, as reader, whatever). We go MSB first, since that @@ -364,7 +361,6 @@ void SendStatus(void) { // Show some leds in a pattern to identify StandAlone mod is running void StandAloneMode(void) { - InStandAloneMode = 1; DbpString("Stand-alone mode! No PC necessary."); // Oooh pretty -- notify user we're in elite samy mode now LED(LED_RED, 200); @@ -403,7 +399,9 @@ void printStandAloneModes(void) { #endif DbpString("Running "); - Dbprintf(" Are we running standalone | %s", (InStandAloneMode)? "Yes" : "No"); + //Dbprintf(" Is Device attached to USB| %s", USB_ATTACHED() ? "Yes" : "No"); + //Dbprintf(" Is USB_reconnect value | %d", GetUSBreconnect() ); + //Dbprintf(" Is USB_configured value | %d", GetUSBconfigured() ); //.. add your own standalone detection based on with compiler directive you are used. // don't "reuse" the already taken ones, this will make things easier when trying to detect the different modes @@ -818,9 +816,14 @@ void UsbPacketReceived(uint8_t *packet, int len) { case CMD_MIFARE_NESTED: MifareNested(c->arg[0], c->arg[1], c->arg[2], c->d.asBytes); break; - case CMD_MIFARE_CHKKEYS: + case CMD_MIFARE_CHKKEYS: { MifareChkKeys(c->arg[0], c->arg[1], c->arg[2], c->d.asBytes); break; + } + case CMD_MIFARE_CHKKEYS_FAST: { + MifareChkKeys_fast(c->arg[0], c->arg[1], c->arg[2], c->d.asBytes); + break; + } case CMD_SIMULATE_MIFARE_CARD: Mifare1ksim(c->arg[0], c->arg[1], c->arg[2], c->d.asBytes); break; @@ -1127,8 +1130,6 @@ void __attribute__((noreturn)) AppMain(void) { common_area.flags.osimage_present = 1; LEDsoff(); - - // list with standalone refs. // Init USB device usb_enable(); @@ -1158,7 +1159,7 @@ void __attribute__((noreturn)) AppMain(void) { byte_t rx[sizeof(UsbCommand)]; size_t rx_len; - + for(;;) { if ( usb_poll_validate_length() ) { rx_len = usb_read(rx, sizeof(UsbCommand)); @@ -1184,7 +1185,9 @@ void __attribute__((noreturn)) AppMain(void) { RunMod(); #endif // when here, we are no longer in standalone mode. - InStandAloneMode = 0; + // reseting the variables which keeps track of usb re-attached/configured + //SetUSBreconnect(0); + //SetUSBconfigured(0); } } } diff --git a/armsrc/apps.h b/armsrc/apps.h index 6efb6095a..2661d32c4 100644 --- a/armsrc/apps.h +++ b/armsrc/apps.h @@ -144,6 +144,7 @@ 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 MifareAcquireNonces(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_fast(uint32_t arg0, uint32_t arg1, uint32_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.c b/armsrc/iso14443a.c index 01abb5a54..0e4abc90c 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -1957,7 +1957,61 @@ int iso14443a_select_card(byte_t *uid_ptr, iso14a_card_select_t *p_card, uint32_ //set default timeout based on ATS iso14a_set_ATS_timeout(resp); } + return 1; +} + +int iso14443a_fast_select_card(uint8_t *uid_ptr, uint8_t num_cascades) { + uint8_t wupa[] = { ISO14443A_CMD_WUPA }; // 0x26 - ISO14443A_CMD_REQA 0x52 - ISO14443A_CMD_WUPA + uint8_t sel_all[] = { ISO14443A_CMD_ANTICOLL_OR_SELECT,0x20 }; + uint8_t sel_uid[] = { ISO14443A_CMD_ANTICOLL_OR_SELECT,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + uint8_t resp[5] = {0}; // theoretically. A usual RATS will be much smaller + uint8_t resp_par[1] = {0}; + uint8_t uid_resp[4] = {0}; + + uint8_t sak = 0x04; // cascade uid + int cascade_level = 0; + + // Broadcast for a card, WUPA (0x52) will force response from all cards in the field + ReaderTransmitBitsPar(wupa, 7, NULL, NULL); + // Receive the ATQA + if(!ReaderReceive(resp, resp_par)) return 0; + + // OK we will select at least at cascade 1, lets see if first byte of UID was 0x88 in + // which case we need to make a cascade 2 request and select - this is a long UID + // While the UID is not complete, the 3nd bit (from the right) is set in the SAK. + for(; sak & 0x04; cascade_level++) { + // SELECT_* (L1: 0x93, L2: 0x95, L3: 0x97) + sel_uid[0] = sel_all[0] = 0x93 + cascade_level * 2; + + if (cascade_level < num_cascades - 1) { + uid_resp[0] = 0x88; + memcpy(uid_resp+1, uid_ptr+cascade_level*3, 3); + } else { + memcpy(uid_resp, uid_ptr+cascade_level*3, 4); + } + + // Construct SELECT UID command + //sel_uid[1] = 0x70; // transmitting a full UID (1 Byte cmd, 1 Byte NVB, 4 Byte UID, 1 Byte BCC, 2 Bytes CRC) + memcpy(sel_uid+2, uid_resp, 4); // the UID received during anticollision, or the provided UID + sel_uid[6] = sel_uid[2] ^ sel_uid[3] ^ sel_uid[4] ^ sel_uid[5]; // calculate and add BCC + AppendCrc14443a(sel_uid, 7); // calculate and add CRC + ReaderTransmit(sel_uid, sizeof(sel_uid), NULL); + + // Receive the SAK + if (!ReaderReceive(resp, resp_par)) return 0; + + sak = resp[0]; + + // Test if more parts of the uid are coming + if ((sak & 0x04) /* && uid_resp[0] == 0x88 */) { + // Remove first byte, 0x88 is not an UID byte, it CT, see page 3 of: + // http://www.nxp.com/documents/application_note/AN10927.pdf + uid_resp[0] = uid_resp[1]; + uid_resp[1] = uid_resp[2]; + uid_resp[2] = uid_resp[3]; + } + } return 1; } diff --git a/armsrc/iso14443a.h b/armsrc/iso14443a.h index 953132d13..bae9ad772 100644 --- a/armsrc/iso14443a.h +++ b/armsrc/iso14443a.h @@ -94,6 +94,7 @@ extern int ReaderReceive(uint8_t *receivedAnswer, uint8_t *par); 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 int iso14443a_fast_select_card(uint8_t *uid_ptr, uint8_t num_cascades); extern void iso14a_set_trigger(bool enable); int EmSendCmd14443aRaw(uint8_t *resp, uint16_t respLen); diff --git a/armsrc/mifarecmd.c b/armsrc/mifarecmd.c index 73ea24e68..c6c2a0e70 100644 --- a/armsrc/mifarecmd.c +++ b/armsrc/mifarecmd.c @@ -773,7 +773,8 @@ void MifareAcquireEncryptedNonces(uint32_t arg0, uint32_t arg1, uint32_t flags, } 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 (!iso14443a_fast_select_card(uid, cascade_levels)) { + //if(!iso14443a_select_card(uid, NULL, NULL, false, cascade_levels, true)) { if (MF_DBGLEVEL >= 1) Dbprintf("AcquireNonces: Can't select card (UID)"); continue; } @@ -1053,6 +1054,315 @@ void MifareNested(uint32_t arg0, uint32_t arg1, uint32_t calibrate, uint8_t *dat // MIFARE check keys. key count up to 85. // //----------------------------------------------------------------------------- + +typedef struct sector_t { + uint8_t keyA[6]; + uint8_t keyB[6]; +} sector_t; + +typedef struct chk_t { + uint64_t key; + uint32_t cuid; + uint8_t cl; + uint8_t block; + uint8_t keyType; + uint8_t *uid; + struct Crypto1State *pcs; +} chk_t; + + +// wait for the card to become ready again +// assume: fast reset of card +/*void chk_timeout(void){ + uint8_t dummy_answer = 0; + ReaderTransmit(&dummy_answer, 1, NULL); + uint32_t timeout = GetCountSspClk() + AUTHENTICATION_TIMEOUT; + while(GetCountSspClk() < timeout); +} +*/ + +#ifndef CHK_TIMEOUT +# define CHK_TIMEOUT() {\ + uint8_t dummy_answer = 0; \ + ReaderTransmit(&dummy_answer, 1, NULL); \ + uint32_t timeout = GetCountSspClk() + AUTHENTICATION_TIMEOUT; \ + while(GetCountSspClk() < timeout); \ +} +#endif + +// checks one key. +// fast select, tries 10times to select +// +// return: +// 2 = failed to select. +// 1 = wrong key +// 0 = correct key +uint8_t chkKey( struct chk_t *c ) { + + uint8_t i = 0, res = 2; + while( i<10 ) { + // this part is from Piwi's faster nonce collecting part in Hardnested. + // assume: fast select + if(!iso14443a_fast_select_card(c->uid, c->cl)) { + ++i; + continue; + } + + res = mifare_classic_authex(c->pcs, c->cuid, c->block, c->keyType, c->key, AUTH_FIRST, NULL, NULL); + CHK_TIMEOUT(); + break; + } + return res; +} + +uint8_t chkKey_readb(struct chk_t *c, uint8_t *keyb) { + + if(!iso14443a_fast_select_card(c->uid, c->cl)) + return 2; + + if ( mifare_classic_authex(c->pcs, c->cuid, c->block, 0, c->key, AUTH_FIRST, NULL, NULL) ) + return 1; + + uint8_t data[16] = {0x00}; + uint8_t res = mifare_classic_readblock(c->pcs, c->cuid, c->block, data); + + CHK_TIMEOUT(); + + // successful read + if ( !res ) { + // data was something else than zeros. + if ( memcmp(data+10, "\x00\x00\x00\x00\x00\x00", 6) != 0) { + memcpy(keyb, data+10, 6); + res = 0; + } else { + res = 3; + } + } + return res; +} + +void chkKey_scanA(struct chk_t *c, struct sector_t *k_sector, uint8_t *found, uint8_t *sectorcnt, uint8_t *foundkeys) { + + // keep track of how many sectors on card. + for (uint8_t s = 0; s < *sectorcnt; ++s) { + + // skip already found A keys + if( !found[(s*2)] ) { + + c->block = FirstBlockOfSector( s ); + uint8_t status = chkKey( c ); + if ( status == 0 ) { + num_to_bytes(c->key, 6, k_sector[s].keyA); + found[(s*2)] = 1; + ++*foundkeys; + } + } + } +} + + void chkKey_scanB(struct chk_t *c, struct sector_t *k_sector, uint8_t *found, uint8_t *sectorcnt, uint8_t *foundkeys) { + + // keep track of how many sectors on card. + for (uint8_t s = 0; s < *sectorcnt; ++s) { + + // skip already found B keys + if( !found[(s*2)+1] ) { + + c->block = FirstBlockOfSector( s ); + uint8_t status = chkKey( c ); + if ( status == 0 ) { + num_to_bytes(c->key, 6, k_sector[s].keyB); + found[(s*2)+1] = 1; + ++*foundkeys; + } + } + } +} + +// loop all A keys, +// when A is found but not B, try to read B. +void chkKey_loopBonly(struct chk_t *c, struct sector_t *k_sector, uint8_t *found, uint8_t *sectorcnt, uint8_t *foundkeys) { + + // read Block B, if A is found. + for (uint8_t s = 0; s < *sectorcnt; ++s) { + c->block = (FirstBlockOfSector( s ) + NumBlocksPerSector( s ) - 1); + // A but not B + if ( found[(s*2)] && !found[(s*2)+1] ){ + c->key = bytes_to_num(k_sector[s].keyA, 6); + uint8_t status = chkKey_readb(c, k_sector[s].keyB); + if ( status == 0 ){ + found[(s*2)+1] = 1; + ++*foundkeys; + // try quick find all B? + // assume: keys comes in groups. Find one B, test against all B. + c->key = bytes_to_num( k_sector[s].keyB, 6); + c->block = 1; + chkKey_scanB(c, k_sector, found, sectorcnt, foundkeys); + } + } + } +} +// get Chunks of keys, to test authentication against card. +// arg0 = antal sectorer +// arg0 = first time +// arg1 = clear trace +// arg2 = antal nycklar i keychunk +// datain = keys as array +void MifareChkKeys_fast(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain) { + + // save old debuglevel, and tempory turn off dbg printing. speedissues. + int OLD_MF_DBGLEVEL = MF_DBGLEVEL; + MF_DBGLEVEL = MF_DBG_NONE; + + // first call or + uint8_t sectorcnt = arg0 & 0xFF; // 16; + uint8_t firstchunk = (arg0 >> 8) & 0xF; + uint8_t lastchunk = (arg0 >> 12) & 0xF; + uint8_t keyCount = arg2 & 0xFF; + uint8_t status = 0; + + struct Crypto1State mpcs = {0, 0}; + struct Crypto1State *pcs; + pcs = &mpcs; + + struct chk_t chk_data; + + uint8_t allkeys = sectorcnt << 1; + + static uint32_t cuid = 0; + static uint8_t cascade_levels = 0; + static uint8_t foundkeys = 0; + static sector_t k_sector[80]; + static uint8_t found[80]; + static uint8_t *uid; + + if (uid == NULL || firstchunk) { + uid = BigBuf_malloc(10); + if (uid == NULL ) { + Dbprintf("ChkKeys: uid malloc failed"); + goto OUT; + } + } + + LEDsoff(); + LED_A_ON(); + + iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); + + if ( firstchunk ) { + + clear_trace(); + set_tracing(false); + + memset(k_sector, 0x00, 480+10); + memset(found, 0x00, 80); + foundkeys = 0; + + iso14a_card_select_t card_info; + if(!iso14443a_select_card(uid, &card_info, &cuid, true, 0, true)) { + Dbprintf("ChkKeys: Can't select card (ALL)"); + goto OUT; + } + switch (card_info.uidlen) { + case 4 : cascade_levels = 1; break; + case 7 : cascade_levels = 2; break; + case 10: cascade_levels = 3; break; + default: break; + } + + CHK_TIMEOUT(); + } + + // set check struct. + chk_data.uid = uid; + chk_data.cuid = cuid; + chk_data.cl = cascade_levels; + chk_data.pcs = pcs; + + // Keychunk loop + for (uint8_t i = 0; i < keyCount; ++i) { + + // Allow button press / usb cmd to interrupt device + if (BUTTON_PRESS() && !usb_poll_validate_length()) break; + + WDT_HIT(); + + // new key + chk_data.key = bytes_to_num(datain + i * 6, 6); + + // Sector main loop + // keep track of how many sectors on card. + for (uint8_t s = 0; s < sectorcnt; ++s) { + + // assume: block0,1,2 has more read rights in accessbits than the sectortrailer. authenticating against block0 in each sector + chk_data.block = FirstBlockOfSector( s ); + + // skip already found A keys + if( !found[(s*2)] ) { + chk_data.keyType = 0; + status = chkKey( &chk_data); + if ( status == 0 ) { + memcpy(k_sector[s].keyA, datain + i * 6, 6); + found[(s*2)] = 1; + ++foundkeys; + + chkKey_scanA(&chk_data, k_sector, found, §orcnt, &foundkeys); + } + } + + // skip already found B keys + if( !found[(s*2)+1] ) { + chk_data.keyType = 1; + status = chkKey( &chk_data); + if ( status == 0 ) { + memcpy(k_sector[s].keyB, datain + i * 6, 6); + found[(s*2)+1] = 1; + ++foundkeys; + + chkKey_scanB(&chk_data, k_sector, found, §orcnt, &foundkeys); + } + } + } // end loop sectors + + // read Block B, if A is found. + chkKey_loopBonly( &chk_data, k_sector, found, §orcnt, &foundkeys); + + // is all keys found? + if ( foundkeys == allkeys ) + break; + } // end loop keys + +OUT: + // restore debug level + MF_DBGLEVEL = OLD_MF_DBGLEVEL; + LEDsoff(); + + // All keys found, send to client, or last keychunk from client + if (foundkeys==allkeys || lastchunk ) { + + uint64_t foo = 0; + uint16_t bar = 0; + for (uint8_t m = 0; m < 64; ++m) + foo |= (found[m] << m); + for (uint8_t m=64; m < sizeof(found); ++m) + bar |= (found[m] << (m-64)); + + uint8_t *tmp = BigBuf_malloc(480+10); + memcpy(tmp, k_sector, sectorcnt * sizeof(sector_t) ); + num_to_bytes(foo, 8, tmp+480); + tmp[488] = bar & 0xFF; + tmp[489] = bar >> 8 & 0xFF; + cmd_send(CMD_ACK, foundkeys, 0, 0, tmp, 480+10); + + set_tracing(false); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + crypto1_destroy(pcs); + } else { + // partial/none keys found + cmd_send(CMD_ACK, foundkeys, 0, 0, 0, 0); + } +} + void MifareChkKeys(uint16_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain) { uint8_t blockNo = arg0 & 0xFF; diff --git a/client/cmdhfmf.c b/client/cmdhfmf.c index e65a97fdb..d5910ca5d 100644 --- a/client/cmdhfmf.c +++ b/client/cmdhfmf.c @@ -128,6 +128,24 @@ int usage_hf14_chk(void){ PrintAndLog(" hf mf chk *1 ? d -- target all blocks, all keys, 1K, write to file"); return 0; } +int usage_hf14_chk_fast(void){ + PrintAndLog("Usage: hf mf fastchk [t|d] [] []"); + PrintAndLog("options:"); + PrintAndLog(" h this help"); + PrintAndLog(" all sectors based on card memory, other values then below defaults to 1k"); + PrintAndLog(" 0 - MINI(320 bytes)"); + PrintAndLog(" 1 - 1K"); + PrintAndLog(" 2 - 2K"); + PrintAndLog(" 4 - 4K"); + PrintAndLog(" d write keys to binary file"); + PrintAndLog(" t write keys to emulator memory\n"); + PrintAndLog(" "); + PrintAndLog("samples:"); + PrintAndLog(" hf mf chk 1 1234567890ab keys.dic -- target 1K using key 1234567890ab, using dictionary file"); + PrintAndLog(" hf mf chk 1 t -- target 1K, write to emulator mem"); + PrintAndLog(" hf mf chk 1 d -- target 1K, write to file"); + return 0; +} int usage_hf14_keybrute(void){ PrintAndLog("J_Run's 2nd phase of multiple sector nested authentication key recovery"); PrintAndLog("You have a known 4 last bytes of a key recovered with mf_nonce_brute tool."); @@ -1147,6 +1165,272 @@ int CmdHF14AMfNestedHard(const char *Cmd) { return 0; } +int randInRange(int min, int max) +{ + return min + (int) (rand() / (double) (RAND_MAX + 1) * (max - min + 1)); +} + +//Fisher–Yates shuffle +void shuffle( uint8_t *array, uint16_t len) { + uint8_t tmp[6]; + uint16_t x; + time_t t; + srand((unsigned) time(&t)); + while (len) { + //x = randInRange(0, len) * (len-- *6) | 0; // 0 = i < n + x = randInRange(0, (len -= 6) ) | 0; // 0 = i < n + x %= 6; + memcpy(tmp, array + x, 6); + memcpy(array + x, array + len, 6); + memcpy(array + len, tmp, 6); + } +} + +int CmdHF14AMfChk_fast(const char *Cmd) { + + if (strlen(Cmd)<2) return usage_hf14_chk_fast(); + + FILE * f; + char filename[FILE_PATH_SIZE]={0}; + char buf[13]; + uint8_t *keyBlock = NULL, *p; + uint16_t stKeyBlock = 20; + int i, keycnt = 0; + int transferToEml = 0, createDumpFile = 0; + char ctmp = 0x00; + uint8_t SectorsCnt = 1; + + uint64_t foo = 0, bar = 0; + icesector_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)); + + // sectors + switch(param_getchar(Cmd, 0)) { + case '0': SectorsCnt = 5; break; + case '1': SectorsCnt = 16; break; + case '2': SectorsCnt = 32; break; + case '4': SectorsCnt = 40; break; + default: SectorsCnt = 16; + } + + ctmp = param_getchar(Cmd, 1); + if (ctmp == 't' || ctmp == 'T') transferToEml = 1; + else if (ctmp == 'd' || ctmp == 'D') createDumpFile = 1; + + for (i = transferToEml || createDumpFile; param_getchar(Cmd, 1 + i); i++) { + if (!param_gethex(Cmd, 1 + i, keyBlock + 6 * keycnt, 12)) { + if ( stKeyBlock - keycnt < 2) { + p = realloc(keyBlock, 6*(stKeyBlock+=10)); + if (!p) { + PrintAndLog("Cannot allocate memory for Keys"); + free(keyBlock); + return 2; + } + keyBlock = p; + } + PrintAndLog("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); + keycnt++; + } else { + // May be a dic file + if ( param_getstr(Cmd, 1 + i,filename) >= FILE_PATH_SIZE ) { + PrintAndLog("File name too long"); + free(keyBlock); + return 2; + } + + if ( (f = fopen( filename , "r")) ) { + while( fgets(buf, sizeof(buf), f) ){ + if (strlen(buf) < 12 || buf[11] == '\n') + continue; + + while (fgetc(f) != '\n' && !feof(f)) ; //goto next line + + if( buf[0]=='#' ) continue; //The line start with # is comment, skip + + if (!isxdigit(buf[0])){ + PrintAndLog("File content error. '%s' must include 12 HEX symbols",buf); + continue; + } + + buf[12] = 0; + if ( stKeyBlock - keycnt < 2) { + p = realloc(keyBlock, 6*(stKeyBlock += 64)); + if (!p) { + PrintAndLog("Cannot allocate memory for defKeys"); + free(keyBlock); + fclose(f); + return 2; + } + keyBlock = p; + } + int pos = 6 * keycnt; + memset(keyBlock + pos, 0, 6); + num_to_bytes(strtoll(buf, NULL, 16), 6, keyBlock + pos); + //PrintAndLog("check key[%2d] %012" PRIx64, keycnt, bytes_to_num(keyBlock + pos, 6) ); + keycnt++; + memset(buf, 0, sizeof(buf)); + } + fclose(f); + PrintAndLog("Loaded %2d keys from %s", keycnt, filename); + } else { + PrintAndLog("File: %s: not found or locked.", filename); + free(keyBlock); + return 1; + + } + } + } + + if (keycnt == 0) { + PrintAndLog("No key specified, trying default keys"); + for (;keycnt < defaultKeysSize; keycnt++) + PrintAndLog("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); + } + + // initialize storage for found keys + e_sector = calloc(SectorsCnt, sizeof(icesector_t)); + if (e_sector == NULL) { + free(keyBlock); + return 1; + } + + // empty e_sector + for(int i = 0; i < SectorsCnt; ++i){ + memset(e_sector[i].keyA, 0xFF, 6); + memset(e_sector[i].keyB, 0xFF, 6); + } + + uint32_t chunksize = keycnt > (USB_CMD_DATA_SIZE/6) ? (USB_CMD_DATA_SIZE/6) : keycnt; + bool firstChunk = true, lastChunk = false; + uint32_t timeout = 0; + + // time + uint64_t t1 = msclock(); + + // main keychunk loop + for (uint32_t i = 0; i < keycnt; i += chunksize) { + + uint32_t size = ((keycnt - i) > chunksize) ? chunksize : keycnt - i; + + // last chunk? + if ( size == keycnt - i) + lastChunk = true; + + // send keychunk + UsbCommand c = {CMD_MIFARE_CHKKEYS_FAST, { (SectorsCnt | (firstChunk << 8) | (lastChunk << 12) ), 0, size}}; + + memcpy(c.d.asBytes, keyBlock + i * 6, 6 * size); + + clearCommandBuffer(); + SendCommand(&c); + UsbCommand resp; + + if ( firstChunk ) firstChunk = false; + + uint64_t t2 = msclock(); + while ( !WaitForResponseTimeout(CMD_ACK, &resp, 2000) ) { + timeout++; + printf("."); + fflush(stdout); + // max timeout for one chunk of 85keys, 60*2sec = 120seconds + // s70 with 40*2 keys to check, 80*85 = 6800 auth. + // takes about 97s, still some margin before abort + if (timeout > 60) { + PrintAndLog("\nNo response from Proxmark. Aborting..."); + return 1; + } + } + + uint8_t curr_keys = resp.arg[0]; + foo = bytes_to_num(resp.d.asBytes+480, 8); + bar = bytes_to_num(resp.d.asBytes+488, 2); + + // reset + timeout = 0; + + t2 = msclock() - t2; + PrintAndLog("\n[-] Chunk: %.1fs | found %d/%d keys", t2, (float)(t2/1000.0), curr_keys, (SectorsCnt<<1)); + + // all keys? + if ( curr_keys == SectorsCnt*2 || lastChunk ) { + memcpy(e_sector, resp.d.asBytes, SectorsCnt * sizeof(icesector_t) ); + break; + } + } + + t1 = msclock() - t1; + PrintAndLog("[+] Time in checkkeys (fast): %.1fs\n", (float)(t1/1000.0)); + + //print keys + printKeyTable_fast( SectorsCnt, e_sector, bar, foo ); + + if (transferToEml) { + uint8_t block[16] = {0x00}; + for (uint8_t i = 0; i < SectorsCnt; ++i ) { + mfEmlGetMem(block, FirstBlockOfSector(i) + NumBlocksPerSector(i) - 1, 1); + /* + if (e_sector[i].foundKey[0]) + memcpy(block, e_sector[i].keyA, 6); + if (e_sector[i].foundKey[1]) + memcpy(block+10, e_sector[i].keyB, 6); + mfEmlSetMem(block, FirstBlockOfSector(i) + NumBlocksPerSector(i) - 1, 1); + */ + } + PrintAndLog("Found keys have been transferred to the emulator memory"); + } + + if (createDumpFile) { + FILE *fkeys = fopen("dumpkeys.bin","wb"); + if (fkeys == NULL) { + PrintAndLog("Could not create file dumpkeys.bin"); + free(keyBlock); + free(e_sector); + return 1; + } + PrintAndLog("Printing keys to binary file dumpkeys.bin..."); + + for( i=0; i> i) & 0x1; + } + for (uint8_t i = 0; i < 16; ++i) { + arr[i+64] = (bar >> i) & 0x1; + } + + PrintAndLog("|---|----------------|---|----------------|---|"); + PrintAndLog("|sec|key A |res|key B |res|"); + PrintAndLog("|---|----------------|---|----------------|---|"); + for (uint8_t i = 0; i < sectorscnt; ++i) { + PrintAndLog("|%03d| %012" PRIx64 " | %d | %012" PRIx64 " | %d |" + , i + , bytes_to_num(e_sector[i].keyA, 6) + , arr[i*2] + , bytes_to_num(e_sector[i].keyB, 6) + , arr[(i*2)+1] + ); + } + PrintAndLog("|---|----------------|---|----------------|---|"); +} + void printKeyTable( uint8_t sectorscnt, sector_t *e_sector ){ PrintAndLog("|---|----------------|---|----------------|---|"); PrintAndLog("|sec|key A |res|key B |res|"); @@ -2611,6 +2922,7 @@ static command_t CommandTable[] = { {"restore", CmdHF14AMfRestore, 0, "Restore MIFARE classic binary file to BLANK tag"}, {"wrbl", CmdHF14AMfWrBl, 0, "Write MIFARE classic block"}, {"chk", CmdHF14AMfChk, 0, "Check keys"}, + {"fchk", CmdHF14AMfChk_fast, 0, "Check keys fast, targets all keys on card"}, {"mifare", CmdHF14AMifare, 0, "Darkside attack. read parity error messages."}, {"nested", CmdHF14AMfNested, 0, "Nested attack. Test nested authentication"}, {"hardnested", CmdHF14AMfNestedHard, 0, "Nested attack for hardened Mifare cards"},