From c3e960269027b4bf1d3d4942d5c0c8eff36e5546 Mon Sep 17 00:00:00 2001 From: hochwasser Date: Sat, 23 Nov 2024 10:26:24 +0100 Subject: [PATCH 01/39] Update mfc_default_keys.dic Keys of the BW Kantine Signed-off-by: hochwasser --- client/dictionaries/mfc_default_keys.dic | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/client/dictionaries/mfc_default_keys.dic b/client/dictionaries/mfc_default_keys.dic index e1d425b80..76b5198b6 100644 --- a/client/dictionaries/mfc_default_keys.dic +++ b/client/dictionaries/mfc_default_keys.dic @@ -2755,3 +2755,8 @@ D37C8F1793F7 543071543071 5F01015F0101 200510241234 +# +# BW Kantine +56cf3acd90ca +542089792be2 +5420aeada758 From 8ae5e7aee1dccd4234934ce080d98e6b8a9c57c0 Mon Sep 17 00:00:00 2001 From: Akury83 <87064827+Akury83@users.noreply.github.com> Date: Sun, 24 Nov 2024 15:45:43 +1100 Subject: [PATCH 02/39] Update t55xx_default_pwds.dic Update to include Chinese cloner ZX-COPY10. Sniffed using the pm3 client: ```[usb] pm3 --> lf t5 sniff -1 [=] T55xx command detection [+] Downlink mode | password | Data | blk | page | 0 | 1 | raw [+] ------------------------+------------+----------+-----+------+-----+-----+------------------------------------------------------------------------------- [+] Default Read | | | 3 | 0 | 18 | 47 | 101011 [+] Default write/pwd read | [00083838] | 00107070 | 0 | 0 | 18 | 46 | 10000000000000100000111000001110000000 [+] Leading 0 pwd write | 00000000 | 00000000 | 0 | 0 | 6 | 70 | 0100000000000000000000000000000000000000000000000000000000000000000000000 [+] Default write/pwd read | [0EAAACAA] | 1D555955 | 1 | 0 | 18 | 46 | 10000011101010101010101100101010101001 [+] Default write/pwd read | [2AB2CB4D] | 5565969A | 2 | 0 | 18 | 46 | 10001010101011001011001011010011010010 [+] Leading 0 pwd write | 00000000 | 00000000 | 0 | 0 | 7 | 16 | 0100000000000000000000000000000000000000000000000000000000000000000000000 [+] Default write/pwd read | [4B352AB2] | 966A5565 | 3 | 0 | 18 | 47 | 10010010110011010100101010101100101011 [+] Leading 0 pwd write | 00000000 | 00000000 | 0 | 0 | 8 | 23 | 0100000000000000000000000000000000000000000000000000000000000000000000000 [+] Default write/pwd read | [3D9EAE24] | 7B3D5C48 | 7 | 0 | 18 | 46 | 10001111011001111010101110001001000111 [+] ----------------------------------------------------------------------------------------------------------------------------------------------------- [usb] pm3 --> lf t5 det -p 7b3d5c48 [=] Chip type......... T55x7 [=] Modulation........ FSK2a [=] Bit rate.......... 4 - RF/50 [=] Inverted.......... Yes [=] Offset............ 33 [=] Seq. terminator... No [=] Block0............ 00107070 (auto detect) [=] Downlink mode..... default/fixed bit length [=] Password set...... Yes [=] Password.......... 7B3D5C48 [usb] pm3 -->``` Signed-off-by: Akury83 <87064827+Akury83@users.noreply.github.com> --- client/dictionaries/t55xx_default_pwds.dic | 2 ++ 1 file changed, 2 insertions(+) diff --git a/client/dictionaries/t55xx_default_pwds.dic b/client/dictionaries/t55xx_default_pwds.dic index 570264306..206e702f7 100644 --- a/client/dictionaries/t55xx_default_pwds.dic +++ b/client/dictionaries/t55xx_default_pwds.dic @@ -22,6 +22,8 @@ F9DCEBA0 89A69E60 # ref lock 314159E0 +#Zonsin ZX-COPY10 +7B3D5C48 # ref. http://www.proxmark.org/forum/viewtopic.php?pid=28115#p28115 AA55BBBB # ref. http://www.proxmark.org/forum/viewtopic.php?pid=33376#p33376 From 2f2667944cc0d0f4f9da238e8f79e42b3cf3f083 Mon Sep 17 00:00:00 2001 From: Antiklesys Date: Sun, 24 Nov 2024 12:59:04 +0800 Subject: [PATCH 03/39] Update cmdhficlass.c --- client/src/cmdhficlass.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index 9ad0e76e2..df0f48f73 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -4280,7 +4280,7 @@ static int CmdHFiClassLegacyRecover(const char *Cmd) { arg_lit0(NULL, "debug", "Re-enables tracing for debugging. Limits cycles to 1."), arg_lit0(NULL, "notest", "Perform real writes on the card!"), arg_lit0(NULL, "allnight", "Loops the loop for 10 times, recommended loop value of 5000."), - arg_lit0(NULL, "sim", "Runs a simulation based on the card's CSN assuming standard key."), + arg_lit0(NULL, "est", "Estimates the key updates based on the card's CSN assuming standard key."), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); From 2aae3317a075cc2dd46017c95f9217356379a0b0 Mon Sep 17 00:00:00 2001 From: Antiklesys Date: Sun, 24 Nov 2024 13:08:28 +0800 Subject: [PATCH 04/39] Added more configcards Added special config cards: 1- Elite Bugger : bugs the reader causing an erroneous and disruptive behavior 2- Added one config card to change the reader's default master key 3- Added a reset master key config card to restore the reader to the default master key --- CHANGELOG.md | 1 + client/src/cmdhficlass.c | 13 ++++++++++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index efe4c5d0a..cdfab6688 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ All notable changes to this project will be documented in this file. This project uses the changelog in accordance with [keepchangelog](http://keepachangelog.com/). Please use this to write notable changes, which is not the same as git commit log... ## [unreleased][unreleased] +- Added special iclass legacy config cards in `hf iclass configcard` (@antiklesys) - Added simulation function to `hf iclass legrec` (@antiklesys) ## [Orca.4.19552][2024-11-22] diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index df0f48f73..a7e215e1e 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -264,7 +264,7 @@ static uint8_t card_app2_limit[] = { 0xff, }; -static iclass_config_card_item_t iclass_config_options[30] = { +static iclass_config_card_item_t iclass_config_options[33] = { //Byte A8 - LED Operations {"(LED) - Led idle (Off) / Led read (Off)", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x18, 0xA8, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, {"(LED) - Led idle (Red) / Led read (Off)", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x18, 0xA8, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, @@ -300,11 +300,18 @@ static iclass_config_card_item_t iclass_config_options[30] = { {"(ELITE Key) - Set ELITE Key and Enable Dual key (Elite + Standard)", {0x0C, 0x00, 0x00, 0x01, 0x00, 0x00, 0xBF, 0x18, 0xBF, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}}, {"(ELITE Key) - Set ELITE Key and ENABLE Keyrolling", {0x0C, 0x00, 0x00, 0x01, 0x00, 0x00, 0xBF, 0x18, 0xBF, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}}, {"(ELITE Key) - Set ELITE Key and DISABLE Standard Key", {0x0C, 0x00, 0x00, 0x01, 0x00, 0x00, 0xBF, 0x18, 0xBF, 0x05, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}}, - //Erroneous / incorrect reader behaviors + //Erroneous / incorrect reader behaviors (read below) + //Elite Bugger: + //Sets block 3 of card 0 presented to the reader to 0, sets block 3 of card 1 presented to the reader to the original value of card 0's block 3 + //Continues setting block 3 of presented cards to block 3 of the previous card the reader scanned + //This renders cards unreadable and hardly recoverable unless the order of the scanned cards is known. + {"(ELITE Bugger) - Renders cards unusable." , {0x0C, 0x00, 0x00, 0x01, 0x00, 0x00, 0xBF, 0x18, 0xBF, 0x02, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}}, //Reset Operations {"(RESET) - Reset READER to defaults", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {"(RESET) - Reset ENROLLER to defaults", {0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF}} + {"(RESET) - Reset ENROLLER to defaults", {0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF}}, //Reader Master Key Operations + {"(MASTER Key) - Change Reader Master Key to Custom Key", {0x28, 0xCB, 0x91, 0x9D, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {"(MASTER Key) - Restore Reader Master Key to Factory Defaults", {0x28, 0xCB, 0x91, 0x9D, 0x00, 0x00, 0x00, 0x1C, 0xE0, 0x5C, 0x91, 0xCF, 0x63, 0x34, 0x23, 0xB9}} }; static const iclass_config_card_item_t *get_config_card_item(int idx) { From c4b8569d87ce8f4d6b9f6f8b1a51dd4feccf538f Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Fri, 22 Nov 2024 20:23:55 +0100 Subject: [PATCH 05/39] fix CmdHF14AMfISEN error handling --- armsrc/mifarecmd.c | 2 +- client/src/cmdhfmf.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/armsrc/mifarecmd.c b/armsrc/mifarecmd.c index 026b509b8..1d8e1f55a 100644 --- a/armsrc/mifarecmd.c +++ b/armsrc/mifarecmd.c @@ -1194,7 +1194,7 @@ out: crypto1_deinit(pcs); LED_B_ON(); if (reply) { - reply_old(CMD_ACK, isOK, cuid, 0, BigBuf_get_EM_addr() + CARD_MEMORY_RF08S_OFFSET, MIFARE_BLOCK_SIZE * (MIFARE_1K_MAXSECTOR + 1)); + reply_mix(CMD_ACK, isOK, cuid, 0, BigBuf_get_EM_addr() + CARD_MEMORY_RF08S_OFFSET, MIFARE_BLOCK_SIZE * (MIFARE_1K_MAXSECTOR + 1)); } LED_B_OFF(); diff --git a/client/src/cmdhfmf.c b/client/src/cmdhfmf.c index f80005c63..6725ddb9a 100644 --- a/client/src/cmdhfmf.c +++ b/client/src/cmdhfmf.c @@ -10007,8 +10007,8 @@ static int CmdHF14AMfISEN(const char *Cmd) { uint64_t t1 = msclock(); uint32_t flags = collect_fm11rf08s_with_data; SendCommandMIX(CMD_HF_MIFARE_ACQ_STATIC_ENCRYPTED_NONCES, flags, 0, 0, key, sizeof(key)); - if (WaitForResponseTimeout(CMD_HF_MIFARE_STATIC_ENCRYPTED_NONCE, &resp, 1000)) { - if (resp.status == PM3_ESOFT) { + if (WaitForResponseTimeout(CMD_ACK, &resp, 1000)) { + if (resp.oldarg[0] != PM3_SUCCESS) { return NONCE_FAIL; } } From 830549b47498ba7e41e3b231ccd430bf594ed439 Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Wed, 20 Nov 2024 09:33:21 +0100 Subject: [PATCH 06/39] hf mf isen: add collect_fm11rf08s_without_backdoor option --- armsrc/appmain.c | 2 +- armsrc/mifarecmd.c | 313 +++++++++++++++++++++++++++++-------------- armsrc/mifarecmd.h | 2 +- armsrc/mifareutil.c | 9 ++ armsrc/mifareutil.h | 1 + client/src/cmdhfmf.c | 16 ++- 6 files changed, 239 insertions(+), 104 deletions(-) diff --git a/armsrc/appmain.c b/armsrc/appmain.c index b258b4ff5..ff38906c5 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -1778,7 +1778,7 @@ static void PacketReceived(PacketCommandNG *packet) { break; } case CMD_HF_MIFARE_ACQ_STATIC_ENCRYPTED_NONCES: { - MifareAcquireStaticEncryptedNonces(packet->oldarg[0], packet->data.asBytes, true); + MifareAcquireStaticEncryptedNonces(packet->oldarg[0], packet->data.asBytes, true, packet->oldarg[1], packet->oldarg[2]); break; } case CMD_HF_MIFARE_ACQ_NONCES: { diff --git a/armsrc/mifarecmd.c b/armsrc/mifarecmd.c index 1d8e1f55a..659c5eec0 100644 --- a/armsrc/mifarecmd.c +++ b/armsrc/mifarecmd.c @@ -1036,7 +1036,7 @@ void MifareAcquireEncryptedNonces(uint32_t arg0, uint32_t arg1, uint32_t flags, // acquire static encrypted nonces in order to perform the attack described in // Philippe Teuwen, "MIFARE Classic: exposing the static encrypted nonce variant" //----------------------------------------------------------------------------- -int MifareAcquireStaticEncryptedNonces(uint32_t flags, const uint8_t *key, bool reply) { +int MifareAcquireStaticEncryptedNonces(uint32_t flags, const uint8_t *key, bool reply, uint8_t first_block_no, uint8_t first_key_type) { struct Crypto1State mpcs = {0, 0}; struct Crypto1State *pcs; pcs = &mpcs; @@ -1055,6 +1055,10 @@ int MifareAcquireStaticEncryptedNonces(uint32_t flags, const uint8_t *key, bool uint8_t buf[MIFARE_BLOCK_SIZE] = {0x00}; uint64_t ui64Key = bytes_to_num(key, 6); bool with_data = flags & 1; + bool without_backdoor = (flags >> 1) & 1; + if (with_data && without_backdoor) { + return PM3_EINVARG; + } uint32_t cuid = 0; int16_t isOK = PM3_SUCCESS; uint8_t cascade_levels = 0; @@ -1072,121 +1076,230 @@ int MifareAcquireStaticEncryptedNonces(uint32_t flags, const uint8_t *key, bool LED_C_ON(); - for (uint16_t sec = 0; sec < MIFARE_1K_MAXSECTOR + 1; sec++) { - uint16_t sec_gap = sec; - if (sec >= MIFARE_1K_MAXSECTOR) { - // gap between user blocks and advanced verification method blocks - sec_gap += 16; + if (without_backdoor) { + uint32_t nt1 = 0; + + iso14a_card_select_t card_info; + if (iso14443a_select_card(uid, &card_info, &cuid, true, 0, true) == 0) { + if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Can't select card (ALL)"); + isOK = PM3_ERFTRANS; + goto out; } - uint16_t blockNo = sec_gap * 4; - for (uint8_t keyType = 0; keyType < 2; keyType++) { - // Test if the action was cancelled - if (BUTTON_PRESS()) { - isOK = PM3_EOPABORTED; + switch (card_info.uidlen) { + case 4 : + cascade_levels = 1; break; + case 7 : + cascade_levels = 2; + break; + case 10: + cascade_levels = 3; + break; + default: + break; + } + if (mifare_classic_authex_cmd(pcs, cuid, first_block_no, MIFARE_AUTH_KEYA + first_key_type, ui64Key, AUTH_FIRST, &nt1, NULL, NULL, NULL, false, false)) { + if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Auth1 error"); + isOK = PM3_ESOFT; + goto out; + }; + + uint16_t len = mifare_sendcmd_short(pcs, AUTH_NESTED, MIFARE_AUTH_KEYA + first_key_type, first_block_no, receivedAnswer, sizeof(receivedAnswer), par_enc, NULL); + if (len != 4) { + if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Auth2 error len=%d", len); + isOK = PM3_ESOFT; + goto out; + } + uint32_t nt_enc = bytes_to_num(receivedAnswer, 4); + + // send some crap to fail auth + CHK_TIMEOUT(); + + if (iso14443a_fast_select_card(uid, cascade_levels) == 0) { + if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Can't select card (UID)"); + isOK = PM3_ERFTRANS; + goto out; + } + if (mifare_classic_authex_cmd(pcs, cuid, first_block_no, MIFARE_AUTH_KEYA + first_key_type, ui64Key, AUTH_FIRST, &nt1, NULL, NULL, NULL, false, false)) { + if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Auth1 error"); + isOK = PM3_ESOFT; + goto out; + }; + // Recover clear nt + struct Crypto1State mpcs_tmp = {0, 0}; + struct Crypto1State *pcs_tmp = &mpcs_tmp; + crypto1_init(pcs_tmp, ui64Key); + uint32_t nt = crypto1_word(pcs_tmp, nt_enc ^ cuid, 1) ^ nt_enc; + int dist = nonce_distance(nt, nt1); + // ref dist is not always stable. Adjust physical distance to maximise ref dist, and try values around estimated nonces... + Dbprintf("Block %2i key %i nested nT=%08x first nT=%08x dist=%i", first_block_no, first_key_type, nt, nt1, dist); + + for (uint16_t sec = 0; sec < MIFARE_1K_MAXSECTOR + 1; sec++) { + uint16_t sec_gap = sec; + if (sec >= MIFARE_1K_MAXSECTOR) { + // gap between user blocks and advanced verification method blocks + sec_gap += 16; } - if (have_uid == false) { // 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) == 0) { - if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Can't select card (ALL)"); - isOK = PM3_ERFTRANS; + uint16_t blockNo = sec_gap * 4; + for (uint8_t keyType = 0; keyType < 2; keyType++) { + // Test if the action was cancelled + if (BUTTON_PRESS()) { + isOK = PM3_EOPABORTED; + break; + } + + len = mifare_sendcmd_short(pcs, AUTH_NESTED, MIFARE_AUTH_KEYA + keyType, blockNo, receivedAnswer, sizeof(receivedAnswer), par_enc, NULL); + if (len != 4) { + if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Auth2 error len=%d", len); + isOK = PM3_ESOFT; 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; - } - have_uid = true; - } else { // no need for anticollision. We can directly select the card + // store nt_enc + memcpy(buf + (keyType * 8) + 4, receivedAnswer, 4); + nt_enc = bytes_to_num(receivedAnswer, 4); + uint8_t nt_par_err = ((((par_enc[0] >> 7) & 1) ^ oddparity8((nt_enc >> 24) & 0xFF)) << 3 | + (((par_enc[0] >> 6) & 1) ^ oddparity8((nt_enc >> 16) & 0xFF)) << 2 | + (((par_enc[0] >> 5) & 1) ^ oddparity8((nt_enc >> 8) & 0xFF)) << 1 | + (((par_enc[0] >> 4) & 1) ^ oddparity8((nt_enc >> 0) & 0xFF))); + // Dbprintf("Sec %2i key %i {nT}=%02x%02x%02x%02x perr=%x", sec, keyType, receivedAnswer[0], receivedAnswer[1], receivedAnswer[2], receivedAnswer[3], nt_par_err); + // store nt_par_err + buf[(keyType * 8) + 2] = nt_par_err; + buf[(keyType * 8) + 3] = 0xAA; // extra check to tell we have nt/nt_enc/par_err + + // send some crap to fail auth + CHK_TIMEOUT(); + if (iso14443a_fast_select_card(uid, cascade_levels) == 0) { if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Can't select card (UID)"); isOK = PM3_ERFTRANS; goto out; } + if (mifare_classic_authex_cmd(pcs, cuid, first_block_no, MIFARE_AUTH_KEYA + first_key_type, ui64Key, AUTH_FIRST, &nt1, NULL, NULL, NULL, false, false)) { + if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Auth1 error"); + isOK = PM3_ESOFT; + goto out; + }; + nt1 = rewind_nonce(nt1, dist); + num_to_bytes(nt1 >> 16, 2, buf + (keyType * 8)); + emlSetMem_xt(buf, (CARD_MEMORY_RF08S_OFFSET / MIFARE_BLOCK_SIZE) + sec, 1, MIFARE_BLOCK_SIZE); } - - uint32_t nt1 = 0; - if (mifare_classic_authex_cmd(pcs, cuid, blockNo, MIFARE_AUTH_KEYA + keyType + 4, ui64Key, AUTH_FIRST, &nt1, NULL, NULL, NULL, false, false)) { - if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Auth1 error"); - isOK = PM3_ESOFT; - goto out; - }; - if ((with_data) && (keyType == 0)) { - uint8_t data[16]; - uint8_t blocks = 4; - if (blockNo >= MIFARE_1K_MAXSECTOR * 4) { - // special RF08S advanced authentication blocks, let's dump in emulator just in case - blocks = 8; + } + } else { + for (uint16_t sec = 0; sec < MIFARE_1K_MAXSECTOR + 1; sec++) { + uint16_t sec_gap = sec; + if (sec >= MIFARE_1K_MAXSECTOR) { + // gap between user blocks and advanced verification method blocks + sec_gap += 16; + } + uint16_t blockNo = sec_gap * 4; + for (uint8_t keyType = 0; keyType < 2; keyType++) { + // Test if the action was cancelled + if (BUTTON_PRESS()) { + isOK = PM3_EOPABORTED; + break; } - for (uint16_t tb = blockNo; tb < blockNo + blocks; tb++) { - memset(data, 0x00, sizeof(data)); - int res = mifare_classic_readblock(pcs, tb, data); - if (res == 1) { - if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Read error"); - isOK = PM3_ESOFT; + if (have_uid == false) { // 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) == 0) { + if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Can't select card (ALL)"); + isOK = PM3_ERFTRANS; + 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; + } + have_uid = true; + } else { // no need for anticollision. We can directly select the card + if (iso14443a_fast_select_card(uid, cascade_levels) == 0) { + if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Can't select card (UID)"); + isOK = PM3_ERFTRANS; goto out; } - emlSetMem_xt(data, tb, 1, 16); } - } - // nested authentication - uint16_t len = mifare_sendcmd_short(pcs, AUTH_NESTED, MIFARE_AUTH_KEYA + keyType + 4, blockNo, receivedAnswer, sizeof(receivedAnswer), par_enc, NULL); - if (len != 4) { - if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Auth2 error len=%d", len); - isOK = PM3_ESOFT; - goto out; - } - uint32_t nt_enc = bytes_to_num(receivedAnswer, 4); - crypto1_init(pcs, ui64Key); - uint32_t nt = crypto1_word(pcs, nt_enc ^ cuid, 1) ^ nt_enc; - // Dbprintf("Sec %2i key %i nT=%08x", sec, keyType + 4, nt); - // store nt (first half) - num_to_bytes(nt >> 16, 2, buf + (keyType * 8)); - // send some crap to fail auth - uint8_t nack[] = {0x04}; - ReaderTransmit(nack, sizeof(nack), NULL); - if (iso14443a_fast_select_card(uid, cascade_levels) == 0) { - if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Can't select card (UID)"); - isOK = PM3_ERFTRANS; - goto out; - } - if (mifare_classic_authex_cmd(pcs, cuid, blockNo, MIFARE_AUTH_KEYA + keyType + 4, ui64Key, AUTH_FIRST, &nt1, NULL, NULL, NULL, false, false)) { - if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Auth1 error"); - isOK = PM3_ESOFT; - goto out; - }; + uint32_t nt1 = 0; + if (mifare_classic_authex_cmd(pcs, cuid, blockNo, MIFARE_AUTH_KEYA + keyType + 4, ui64Key, AUTH_FIRST, &nt1, NULL, NULL, NULL, false, false)) { + if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Auth1 error"); + isOK = PM3_ESOFT; + goto out; + }; + if ((with_data) && (keyType == 0)) { + uint8_t data[16]; + uint8_t blocks = 4; + if (blockNo >= MIFARE_1K_MAXSECTOR * 4) { + // special RF08S advanced authentication blocks, let's dump in emulator just in case + blocks = 8; + } + for (uint16_t tb = blockNo; tb < blockNo + blocks; tb++) { + memset(data, 0x00, sizeof(data)); + int res = mifare_classic_readblock(pcs, tb, data); + if (res == 1) { + if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Read error"); + isOK = PM3_ESOFT; + goto out; + } + emlSetMem_xt(data, tb, 1, 16); + } + } + // nested authentication + uint16_t len = mifare_sendcmd_short(pcs, AUTH_NESTED, MIFARE_AUTH_KEYA + keyType + 4, blockNo, receivedAnswer, sizeof(receivedAnswer), par_enc, NULL); + if (len != 4) { + if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Auth2 error len=%d", len); + isOK = PM3_ESOFT; + goto out; + } + uint32_t nt_enc = bytes_to_num(receivedAnswer, 4); + crypto1_init(pcs, ui64Key); + uint32_t nt = crypto1_word(pcs, nt_enc ^ cuid, 1) ^ nt_enc; + // Dbprintf("Sec %2i key %i nT=%08x", sec, keyType + 4, nt); + // store nt (first half) + num_to_bytes(nt >> 16, 2, buf + (keyType * 8)); + // send some crap to fail auth + CHK_TIMEOUT(); - // nested authentication on regular keytype - len = mifare_sendcmd_short(pcs, AUTH_NESTED, MIFARE_AUTH_KEYA + keyType, blockNo, receivedAnswer, sizeof(receivedAnswer), par_enc, NULL); - if (len != 4) { - if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Auth2 error len=%d", len); - isOK = PM3_ESOFT; - goto out; + if (iso14443a_fast_select_card(uid, cascade_levels) == 0) { + if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Can't select card (UID)"); + isOK = PM3_ERFTRANS; + goto out; + } + if (mifare_classic_authex_cmd(pcs, cuid, blockNo, MIFARE_AUTH_KEYA + keyType + 4, ui64Key, AUTH_FIRST, &nt1, NULL, NULL, NULL, false, false)) { + if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Auth1 error"); + isOK = PM3_ESOFT; + goto out; + }; + + // nested authentication on regular keytype + len = mifare_sendcmd_short(pcs, AUTH_NESTED, MIFARE_AUTH_KEYA + keyType, blockNo, receivedAnswer, sizeof(receivedAnswer), par_enc, NULL); + if (len != 4) { + if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Auth2 error len=%d", len); + isOK = PM3_ESOFT; + goto out; + } + // store nt_enc + memcpy(buf + (keyType * 8) + 4, receivedAnswer, 4); + nt_enc = bytes_to_num(receivedAnswer, 4); + uint8_t nt_par_err = ((((par_enc[0] >> 7) & 1) ^ oddparity8((nt_enc >> 24) & 0xFF)) << 3 | + (((par_enc[0] >> 6) & 1) ^ oddparity8((nt_enc >> 16) & 0xFF)) << 2 | + (((par_enc[0] >> 5) & 1) ^ oddparity8((nt_enc >> 8) & 0xFF)) << 1 | + (((par_enc[0] >> 4) & 1) ^ oddparity8((nt_enc >> 0) & 0xFF))); + // Dbprintf("Sec %2i key %i {nT}=%02x%02x%02x%02x perr=%x", sec, keyType, receivedAnswer[0], receivedAnswer[1], receivedAnswer[2], receivedAnswer[3], nt_par_err); + // store nt_par_err + buf[(keyType * 8) + 2] = nt_par_err; + buf[(keyType * 8) + 3] = 0xAA; // extra check to tell we have nt/nt_enc/par_err + emlSetMem_xt(buf, (CARD_MEMORY_RF08S_OFFSET / MIFARE_BLOCK_SIZE) + sec, 1, MIFARE_BLOCK_SIZE); + // send some crap to fail auth + CHK_TIMEOUT(); } - // store nt_enc - memcpy(buf + (keyType * 8) + 4, receivedAnswer, 4); - nt_enc = bytes_to_num(receivedAnswer, 4); - uint8_t nt_par_err = ((((par_enc[0] >> 7) & 1) ^ oddparity8((nt_enc >> 24) & 0xFF)) << 3 | - (((par_enc[0] >> 6) & 1) ^ oddparity8((nt_enc >> 16) & 0xFF)) << 2 | - (((par_enc[0] >> 5) & 1) ^ oddparity8((nt_enc >> 8) & 0xFF)) << 1 | - (((par_enc[0] >> 4) & 1) ^ oddparity8((nt_enc >> 0) & 0xFF))); - // Dbprintf("Sec %2i key %i {nT}=%02x%02x%02x%02x perr=%x", sec, keyType, receivedAnswer[0], receivedAnswer[1], receivedAnswer[2], receivedAnswer[3], nt_par_err); - // store nt_par_err - buf[(keyType * 8) + 2] = nt_par_err; - buf[(keyType * 8) + 3] = 0xAA; // extra check to tell we have nt/nt_enc/par_err - emlSetMem_xt(buf, (CARD_MEMORY_RF08S_OFFSET / MIFARE_BLOCK_SIZE) + sec, 1, MIFARE_BLOCK_SIZE); - // send some crap to fail auth - ReaderTransmit(nack, sizeof(nack), NULL); } } out: @@ -3127,7 +3240,8 @@ void MifareHasStaticEncryptedNonce(uint8_t block_no, uint8_t key_type, uint8_t * goto OUT; }; first_nt_counter++; - } else for (uint8_t i = 0; i < nr_nested; i++) { + } else { + for (uint8_t i = 0; i < nr_nested; i++) { if (need_first_auth) { cuid = 0; @@ -3204,6 +3318,7 @@ void MifareHasStaticEncryptedNonce(uint8_t block_no, uint8_t key_type, uint8_t * } oldntenc = ntenc; } + } data[1] = (cuid >> 24) & 0xFF; data[2] = (cuid >> 16) & 0xFF; diff --git a/armsrc/mifarecmd.h b/armsrc/mifarecmd.h index 8f19528c2..2dcfa4e4b 100644 --- a/armsrc/mifarecmd.h +++ b/armsrc/mifarecmd.h @@ -37,7 +37,7 @@ void MifareNested(uint8_t blockNo, uint8_t keyType, uint8_t targetBlockNo, uint8 void MifareStaticNested(uint8_t blockNo, uint8_t keyType, uint8_t targetBlockNo, uint8_t targetKeyType, uint8_t *key); void MifareAcquireEncryptedNonces(uint32_t arg0, uint32_t arg1, uint32_t flags, uint8_t *datain); -int MifareAcquireStaticEncryptedNonces(uint32_t flags, const uint8_t *key, bool reply); +int MifareAcquireStaticEncryptedNonces(uint32_t flags, const uint8_t *key, bool reply, uint8_t first_block_no, uint8_t first_key_type); void MifareAcquireNonces(uint32_t arg0, uint32_t flags); void MifareChkKeys(uint8_t *datain, uint8_t reserved_mem); void MifareChkKeys_fast(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain); diff --git a/armsrc/mifareutil.c b/armsrc/mifareutil.c index 9f4b87674..b8cb4838d 100644 --- a/armsrc/mifareutil.c +++ b/armsrc/mifareutil.c @@ -983,3 +983,12 @@ int nonce_distance(uint32_t from, uint32_t to) { int nonce16_index(uint16_t nt) { return nonce16_distance(0x0100, nt) + 1; } + +uint32_t rewind_nonce(uint32_t from, uint16_t dist) { + uint16_t x = from >> 16; + for (uint16_t i = 0; i < dist; i++) { + x = ((x << 1 | x >> 15) & 0xffff) ^ ((x >> 1 ^ x >> 2 ^ x >> 4) & 0x100); + } + uint32_t nt = x; + return nt << 16 | prng_successor(nt, 16); +} diff --git a/armsrc/mifareutil.h b/armsrc/mifareutil.h index b1ae83021..d118533a9 100644 --- a/armsrc/mifareutil.h +++ b/armsrc/mifareutil.h @@ -128,4 +128,5 @@ bool validate_parity_nonce(uint32_t ntenc, uint8_t ntparenc, uint32_t nt); int nonce_distance(uint32_t from, uint32_t to); int nonce16_distance(uint16_t x, uint16_t y); int nonce16_index(uint16_t nt); +uint32_t rewind_nonce(uint32_t from, uint16_t dist); #endif diff --git a/client/src/cmdhfmf.c b/client/src/cmdhfmf.c index 6725ddb9a..2bfd6a020 100644 --- a/client/src/cmdhfmf.c +++ b/client/src/cmdhfmf.c @@ -9883,6 +9883,7 @@ static int CmdHF14AMfISEN(const char *Cmd) { arg_rem("FM11RF08S specific options:", "Incompatible with above options, except -k; output in JSON"), arg_lit0(NULL, "collect_fm11rf08s", "collect all nT/{nT}/par_err."), arg_lit0(NULL, "collect_fm11rf08s_with_data", "collect all nT/{nT}/par_err and data blocks."), + arg_lit0(NULL, "collect_fm11rf08s_without_backdoor", "collect all nT/{nT}/par_err without backdoor. Requires first auth keytype and block"), arg_str0("f", "file", "", "Specify a filename for collected data"), arg_param_end }; @@ -9954,9 +9955,18 @@ static int CmdHF14AMfISEN(const char *Cmd) { if (collect_fm11rf08s_with_data) { collect_fm11rf08s = 1; } + bool collect_fm11rf08s_without_backdoor = arg_get_lit(ctx, 23); + if (collect_fm11rf08s_without_backdoor) { + collect_fm11rf08s = 1; + } + if (collect_fm11rf08s_with_data && collect_fm11rf08s_without_backdoor) { + CLIParserFree(ctx); + PrintAndLogEx(WARNING, "Don't mix with_data and without_backdoor options"); + return PM3_EINVARG; + } int fnlen = 0; char filename[FILE_PATH_SIZE] = {0}; - CLIParamStrToBuf(arg_get_str(ctx, 23), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); + CLIParamStrToBuf(arg_get_str(ctx, 24), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); CLIParserFree(ctx); @@ -10005,8 +10015,8 @@ static int CmdHF14AMfISEN(const char *Cmd) { if (collect_fm11rf08s) { uint64_t t1 = msclock(); - uint32_t flags = collect_fm11rf08s_with_data; - SendCommandMIX(CMD_HF_MIFARE_ACQ_STATIC_ENCRYPTED_NONCES, flags, 0, 0, key, sizeof(key)); + uint32_t flags = collect_fm11rf08s_with_data | (collect_fm11rf08s_without_backdoor << 1); + SendCommandMIX(CMD_HF_MIFARE_ACQ_STATIC_ENCRYPTED_NONCES, flags, blockn, keytype, key, sizeof(key)); if (WaitForResponseTimeout(CMD_ACK, &resp, 1000)) { if (resp.oldarg[0] != PM3_SUCCESS) { return NONCE_FAIL; From 10d8ae13cac4644756b21d60774dbdbd3e2e45dc Mon Sep 17 00:00:00 2001 From: ry4000 <154689120+ry4000@users.noreply.github.com> Date: Tue, 26 Nov 2024 18:08:48 +1100 Subject: [PATCH 07/39] R&Y: Added GRB Tap-N-Go to aid_desfire.json - Added GRB Tap-N-Go F21201 and F21202 to aid_desfire.json. Thank you. -R&Y. Signed-off-by: ry4000 <154689120+ry4000@users.noreply.github.com> --- client/resources/aid_desfire.json | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/client/resources/aid_desfire.json b/client/resources/aid_desfire.json index 565c51b0e..53cb47992 100644 --- a/client/resources/aid_desfire.json +++ b/client/resources/aid_desfire.json @@ -1151,6 +1151,22 @@ "Description": "FIDs 02: Card Balance; 04: Refill History; 08: Card Information; 0E: Trip History", "Type": "transport" }, + { + "AID": "F21201", + "Vendor": "Green Bay Metro Transit via Genfare", + "Country": "US", + "Name": "Tap-N-Go Card (GRB)", + "Description": "GRB Tap-N-Go Card", + "Type": "transport" + }, + { + "AID": "F21202", + "Vendor": "Green Bay Metro Transit via Genfare", + "Country": "US", + "Name": "Tap-N-Go Card (GRB)", + "Description": "GRB Tap-N-Go Card", + "Type": "transport" + }, { "AID": "F21360", "Vendor": "INIT", From d39775ca46d874a28456342b53079a9e5103a2fb Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Tue, 26 Nov 2024 12:41:05 +0100 Subject: [PATCH 08/39] recover_pk.py: replace secp192r1 by prime192v1 --- tools/recover_pk.py | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/tools/recover_pk.py b/tools/recover_pk.py index 9d9c816fd..a1dffc9eb 100755 --- a/tools/recover_pk.py +++ b/tools/recover_pk.py @@ -12,12 +12,13 @@ from colors import color debug = False + def guess_curvename(signature): siglen = (len(signature) // 2) & 0xfe if siglen == 32: curves = ["secp128r1", "secp128r2"] elif siglen == 48: - curves = ["secp192k1", "secp192r1"] + curves = ["secp192k1", "prime192v1"] elif siglen == 56: curves = ["secp224k1", "secp224r1"] elif siglen == 64: @@ -31,6 +32,7 @@ def guess_curvename(signature): raise ValueError("Unsupported signature size %s" % lenstr) return curves + def recover(data, signature, curvename, alghash=None): recovered = set() try: @@ -60,6 +62,7 @@ def recover(data, signature, curvename, alghash=None): pass return recovered + def recover_multiple(uids, sigs, curvename, alghash=None): recovered = set() assert len(uids) == len(sigs) @@ -82,6 +85,7 @@ def recover_multiple(uids, sigs, curvename, alghash=None): recovered &= recovered_tmp return recovered + def selftests(): tests = [ {'name': "Mifare Ultralight EV1", @@ -158,16 +162,16 @@ def selftests(): # 'samples': ["aa", "DF0E506DFF8FCFC4B7B979D917644445F1230D2C7CDC342AFA842CA240C210BE7275F62073A9670F2DCEFC602CBEE771C2B4CD4A04F3D1EA11F49ABDF7E8B721"], # 'pk': ""}, {'name': "MIFARE Plus Trojka", - # uses secp224r1, None, - 'samples': ["04B59F6A226F82", "6F577EB7F570D74DB6250477427F68A0088762BD318767537122919A7916597149F9D16D8B135E9BF826FB28AE293F3168661CD4A049FAED", - "04B44A82D80F92", "A0868ECF26733D3C3C838D055968B4559F77693CC3E346E3A4741BC826801F8360FD88857BEC440AAD3A21153D64302DEB6F5ED40B15C3F7"], - 'pk': "040F732E0EA7DF2B38F791BF89425BF7DCDF3EE4D976669E3831F324FF15751BD52AFF1782F72FF2731EEAD5F63ABE7D126E03C856FFB942AF"}, + # uses secp224r1, None, + 'samples': ["04B59F6A226F82", "6F577EB7F570D74DB6250477427F68A0088762BD318767537122919A7916597149F9D16D8B135E9BF826FB28AE293F3168661CD4A049FAED", + "04B44A82D80F92", "A0868ECF26733D3C3C838D055968B4559F77693CC3E346E3A4741BC826801F8360FD88857BEC440AAD3A21153D64302DEB6F5ED40B15C3F7"], + 'pk': "040F732E0EA7DF2B38F791BF89425BF7DCDF3EE4D976669E3831F324FF15751BD52AFF1782F72FF2731EEAD5F63ABE7D126E03C856FFB942AF"}, -# {'name': "MIFARE Ultralight AES", - # uses NID_secp192r1, OpenSSL doesn't support it. This is commented out until that day. -# 'samples': ["045E4CC2451390", "C9BBDA1B99EB6634CDFD8E3251AC5C4742EA5FA507B8A8A8B39B19AB7340D173331589C54C56C49F0CCA6DDBAC1E492A", -# "043F88C2451390", "5C2055A7373F119C3FDD9843020B06AA0E6DE18C16496C425C4AD971A50F05FA1A67B9E39CA60C355EEEEBF8214A84A5"], -# 'pk': "0453BF8C49B7BD9FE3207A91513B9C1D238ECAB07186B772104AB535F7D3AE63CF7C7F3DD0D169DA3E99E43C6399621A86"}, + {'name': "MIFARE Ultralight AES", + # uses prime192v1, None, + 'samples': ["045E4CC2451390", "C9BBDA1B99EB6634CDFD8E3251AC5C4742EA5FA507B8A8A8B39B19AB7340D173331589C54C56C49F0CCA6DDBAC1E492A", + "043F88C2451390", "5C2055A7373F119C3FDD9843020B06AA0E6DE18C16496C425C4AD971A50F05FA1A67B9E39CA60C355EEEEBF8214A84A5"], + 'pk': "0453BF8C49B7BD9FE3207A91513B9C1D238ECAB07186B772104AB535F7D3AE63CF7C7F3DD0D169DA3E99E43C6399621A86"}, {'name': "MIFARE Classic / QL88", 'samples': ["30933C61", "AEA4DD0B800FAC63D4DE08EE91F4650ED825FD6B4D7DEEE98DBC9BAE10BE003E", @@ -234,13 +238,15 @@ def selftests(): print("Tests: ( %s )" % [fail, ok][succeeded]) print("") + if __name__ == "__main__": if len(sys.argv) == 2 and sys.argv[1] == "selftests": selftests() exit(0) if len(sys.argv) < 3 or len(sys.argv) % 2 == 0: print("Usage: \n%s UID SIGN [UID SIGN] [...]" % sys.argv[0]) - print("Example: \n%s 04ee45daa34084 ebb6102bff74b087d18a57a54bc375159a04ea9bc61080b7f4a85afe1587d73b" % sys.argv[0]) + print("Example: \n%s 04ee45daa34084 ebb6102bff74b087d18a57a54bc375159a04ea9bc61080b7f4a85afe1587d73b" + % sys.argv[0]) exit(1) uids, sigs = sys.argv[1:][::2], sys.argv[1:][1::2] once = True From f0b93405fa12299c37d86db8d566dc246c266dda Mon Sep 17 00:00:00 2001 From: douniwan5788 Date: Wed, 27 Nov 2024 00:01:15 +0800 Subject: [PATCH 09/39] fix Hitag S concatbits and `lf em 410x clone --hts` --- client/src/cmdlfem410x.c | 1 + common/commonutil.c | 4 ++-- include/hitag.h | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/client/src/cmdlfem410x.c b/client/src/cmdlfem410x.c index ebcd9ee8c..10ad41369 100644 --- a/client/src/cmdlfem410x.c +++ b/client/src/cmdlfem410x.c @@ -732,6 +732,7 @@ static int CmdEM410xClone(const char *Cmd) { packet.cmd = HTSF_82xx; memcpy(packet.pwd, "\xBB\xDD\x33\x99", HITAGS_PAGE_SIZE); + packet.mode = HITAGS_UID_REQ_FADV; SendCommandNG(CMD_LF_HITAGS_WRITE, (uint8_t *)&packet, sizeof(packet)); if (WaitForResponseTimeout(CMD_LF_HITAGS_WRITE, &resp, 4000) == false) { PrintAndLogEx(WARNING, "timeout while waiting for reply."); diff --git a/common/commonutil.c b/common/commonutil.c index ad867be86..7ed34067f 100644 --- a/common/commonutil.c +++ b/common/commonutil.c @@ -554,8 +554,8 @@ size_t concatbits(uint8_t *dest, int dest_offset, const uint8_t *src, int src_of end = nbits; step = 1; } else { - i = nbits; - end = 0; + i = nbits - 1; + end = -1; step = -1; } diff --git a/include/hitag.h b/include/hitag.h index 451952ce4..e63b8c448 100644 --- a/include/hitag.h +++ b/include/hitag.h @@ -161,7 +161,7 @@ typedef struct { uint8_t logdata_1[4]; uint8_t nonce[4]; - //Hitag s section + // Hitag S section uint8_t mode; } PACKED lf_hitag_data_t; From 0fa8351fa9fe8eedfc5b8c7ddaf05fc52017602c Mon Sep 17 00:00:00 2001 From: Piotr Rzeszut Date: Thu, 28 Nov 2024 01:01:11 +0100 Subject: [PATCH 10/39] Add display of maximum read/write block in configuration parsing in hf_mf_ultimatecard.lua --- client/luascripts/hf_mf_ultimatecard.lua | 2 ++ 1 file changed, 2 insertions(+) diff --git a/client/luascripts/hf_mf_ultimatecard.lua b/client/luascripts/hf_mf_ultimatecard.lua index 09040eea5..1226e9b07 100644 --- a/client/luascripts/hf_mf_ultimatecard.lua +++ b/client/luascripts/hf_mf_ultimatecard.lua @@ -186,6 +186,7 @@ local function read_config() end -- extract data from CONFIG - based on CONFIG in https://github.com/RfidResearchGroup/proxmark3/blob/master/doc/magic_cards_notes.md#gen-4-gtu ulprotocol, uidlength, readpass, gtumode, ats, atqa1, atqa2, sak, ulmode = magicconfig:sub(1,2), magicconfig:sub(3,4), magicconfig:sub(5,12), magicconfig:sub(13,14), magicconfig:sub(15,48), magicconfig:sub(51,52), magicconfig:sub(49,50), magicconfig:sub(53,54), magicconfig:sub(55,56) + maxRWblk = magicconfig:sub(57, 58) atqaf = atqa1..' '..atqa2 cardtype, cardprotocol, gtustr, atsstr = 'unknown', 'unknown', 'unknown', 'unknown' if magicconfig == nil then lib14a.disconnect(); return nil, "can't read configuration, "..err_lock end @@ -291,6 +292,7 @@ local function read_config() print(' - Version ', cversion) print(' - Signature ', signature1..signature2) end + print(' - Max R/W Block ', maxRWblk) end lib14a.disconnect() return true, 'Ok' From bf1726a31d7c40e0d14ffc8867ab4b727d12608a Mon Sep 17 00:00:00 2001 From: Piotr Rzeszut Date: Thu, 28 Nov 2024 01:02:41 +0100 Subject: [PATCH 11/39] Add option to set maximum read/write block using hf_mf_ultimatecard.lua --- client/luascripts/hf_mf_ultimatecard.lua | 31 ++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/client/luascripts/hf_mf_ultimatecard.lua b/client/luascripts/hf_mf_ultimatecard.lua index 1226e9b07..064a30282 100644 --- a/client/luascripts/hf_mf_ultimatecard.lua +++ b/client/luascripts/hf_mf_ultimatecard.lua @@ -75,7 +75,11 @@ arguments = [[ -z ATS (<1b length><0-16 ATS> hexsymbols), Configure ATS. Length set to 00 will disable ATS. -w Wipe tag. 0 for Mifare or 1 for UL. Fills tag with zeros and put default values for type selected. -m Ultralight mode (00 UL EV1, 01 NTAG, 02 UL-C, 03 UL) Set type of UL. - -n Ultralight protocol (00 MFC, 01 UL), switches between UL and MFC mode + -n Ultralight protocol (00 MFC, 01 UL), switches between UL and MFC mode]] +-- Need to split because reached maximum string length processed by lua +arguments2 = [[ + -b Set maximum read/write blocks (2 hexsymbols) + NOTE: Ultralight EV1 and NTAG Version info and Signature are stored respectively in blocks 250-251 and 242-249 -k Ultimate Magic Card Key (IF DIFFERENT THAN DEFAULT 00000000) ]] --- @@ -110,6 +114,7 @@ local function help() print(usage) print(ansicolors.cyan..'Arguments'..ansicolors.reset) print(arguments) + print(arguments2) print(ansicolors.cyan..'Example usage'..ansicolors.reset) print(example) end @@ -1012,13 +1017,33 @@ local function wipe(wtype) end end --- +-- Write maximum read/write block number, +local function write_maxRWblk(data) + -- input number check + if data == nil then return nil, 'empty block number' end + if #data == 0 then return nil, 'empty block number' end + if #data ~= 2 then return nil, 'block number wrong length. Should be 1 hex byte' end + + print('Set max R/W block', data) + local info = connect() + if not info then return false, "Can't select card" end + local resp + -- set maximum read/write block + resp = send("CF".._key.."6B"..data) + lib14a.disconnect() + if resp ~= '9000FD07' then return nil, 'Failed to write maximum read/write block' + else + return true, 'Ok' + end +end +--- -- The main entry point function main(args) print() local err, msg if #args == 0 then return help() end -- Read the parameters - for o, a in getopt.getopt(args, 'hck:u:t:p:a:s:o:v:q:g:z:n:m:w:') do + for o, a in getopt.getopt(args, 'hck:u:t:p:a:s:o:v:q:g:z:n:m:w:b:') do -- help if o == "h" then return help() end -- set Ultimate Magic Card Key for read write @@ -1051,6 +1076,8 @@ function main(args) if o == "m" then err, msg = write_ulm(a) end -- write UL protocol if o == "n" then err, msg = write_ulp(a) end + -- write max r/w block + if o == "b" then err, msg = write_maxRWblk(a) end if err == nil then return oops(msg) end end end From be21154d8384543ba1048ccf10b1d2ffd2ee0d16 Mon Sep 17 00:00:00 2001 From: Piotr Rzeszut Date: Thu, 28 Nov 2024 01:04:18 +0100 Subject: [PATCH 12/39] Refactor arguments help of hf_mf_ultimatecard.lua, done because already had to split the string due to reaching limit of string length in lua. --- client/luascripts/hf_mf_ultimatecard.lua | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/client/luascripts/hf_mf_ultimatecard.lua b/client/luascripts/hf_mf_ultimatecard.lua index 064a30282..a35b24173 100644 --- a/client/luascripts/hf_mf_ultimatecard.lua +++ b/client/luascripts/hf_mf_ultimatecard.lua @@ -51,17 +51,17 @@ arguments = [[ -u UID (8-20 hexsymbols), set UID on tag -t tag type to impersonate 1 = Mifare Mini S20 4-byte - 2 = Mifare Mini S20 7-byte 15 = NTAG 210 - 3 = Mifare Mini S20 10-byte 16 = NTAG 212 - 4 = Mifare 1k S50 4-byte 17 = NTAG 213 - 5 = Mifare 1k S50 7-byte 18 = NTAG 215 - 6 = Mifare 1k S50 10-byte 19 = NTAG 216 - 7 = Mifare 4k S70 4-byte 20 = NTAG I2C 1K - 8 = Mifare 4k S70 7-byte 21 = NTAG I2C 2K - 9 = Mifare 4k S70 10-byte 22 = NTAG I2C 1K PLUS - *** 10 = UL - NOT WORKING FULLY 23 = NTAG I2C 2K PLUS - *** 11 = UL-C - NOT WORKING FULLY 24 = NTAG 213F - 12 = UL EV1 48b 25 = NTAG 216F + 2 = Mifare Mini S20 7-byte | 15 = NTAG 210 + 3 = Mifare Mini S20 10-byte | 16 = NTAG 212 + 4 = Mifare 1k S50 4-byte | 17 = NTAG 213 + 5 = Mifare 1k S50 7-byte | 18 = NTAG 215 + 6 = Mifare 1k S50 10-byte | 19 = NTAG 216 + 7 = Mifare 4k S70 4-byte | 20 = NTAG I2C 1K + 8 = Mifare 4k S70 7-byte | 21 = NTAG I2C 2K + 9 = Mifare 4k S70 10-byte | 22 = NTAG I2C 1K PLUS + *** 10 = UL - NOT WORKING FULLY | 23 = NTAG I2C 2K PLUS + *** 11 = UL-C - NOT WORKING FULLY | 24 = NTAG 213F + 12 = UL EV1 48b | 25 = NTAG 216F 13 = UL EV1 128b *** 14 = UL Plus - NOT WORKING YET From 0eb86f2c5896cab091ea15cf98c2e8dbbbefecac Mon Sep 17 00:00:00 2001 From: Piotr Rzeszut Date: Thu, 28 Nov 2024 01:10:36 +0100 Subject: [PATCH 13/39] Add info about changes in hf_mf_ultimatecard.lua (set/get maximum read/write block) to Changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index cdfab6688..575e97327 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ All notable changes to this project will be documented in this file. This project uses the changelog in accordance with [keepchangelog](http://keepachangelog.com/). Please use this to write notable changes, which is not the same as git commit log... ## [unreleased][unreleased] +- Add option to set and get maximum read/write block number using `hf_mf_ultimatecard` script (@piotrva) - Added special iclass legacy config cards in `hf iclass configcard` (@antiklesys) - Added simulation function to `hf iclass legrec` (@antiklesys) From 639f16ba3b0f43e6f6766474523b0b9b7c38b184 Mon Sep 17 00:00:00 2001 From: ANTodorov Date: Thu, 28 Nov 2024 10:55:54 +0200 Subject: [PATCH 14/39] added JEDEC information for SPI flash W25Q64JV added some "extrapolated" but logical matches by Manufacturer/Device ID only some reordering --- CHANGELOG.md | 1 + common_arm/flashmem.h | 41 +++++++++++++++++++++++------------------ 2 files changed, 24 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cdfab6688..b4afc52ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ All notable changes to this project will be documented in this file. This project uses the changelog in accordance with [keepchangelog](http://keepachangelog.com/). Please use this to write notable changes, which is not the same as git commit log... ## [unreleased][unreleased] +- Added JEDEC information for SPI flash W25Q64JV (@ANTodorov) - Added special iclass legacy config cards in `hf iclass configcard` (@antiklesys) - Added simulation function to `hf iclass legrec` (@antiklesys) diff --git a/common_arm/flashmem.h b/common_arm/flashmem.h index b28989646..616575e49 100644 --- a/common_arm/flashmem.h +++ b/common_arm/flashmem.h @@ -146,28 +146,33 @@ typedef struct { static const spi_flash_t SpiFlashTable[] = { // first element is the default of 4 * 64kB pages (256kB) - { 0x00, 0x00, 0x0000, 4, "unknown" }, // 256k + { 0x00, 0x00, 0x0000, 4, "unknown" }, // 256k // Manufacturer: Puya - { 0x85, 0x00, 0x6015, 32, "P25Q16H" }, // 2048k - /// Manufacturer: Renesas - { 0x1F, 0x46, 0x0000, 32, "AT25XE161D" }, // 2048k - { 0x1F, 0x47, 0x0000, 64, "AT25XE321D" }, // 4096k + { 0x85, 0x14, 0x6015, 32, "P25Q16H" }, // 2048k // Manufacturer: Winbond - { 0xEF, 0x00, 0x3012, 4, "W25X20BV" }, // 256k - { 0xEF, 0x00, 0x3013, 8, "W25X40BV" }, // 512k + { 0xEF, 0x00, 0x3012, 4, "W25X20BV" }, // 256k + { 0xEF, 0x00, 0x3013, 8, "W25X40BV" }, // 512k - { 0xEF, 0x00, 0x4013, 8, "W25Q40BV" }, // 512k - { 0xEF, 0x00, 0x4014, 16, "W25Q80BV" }, // 1024k - { 0xEF, 0x14, 0x4015, 32, "W25Q16BV" }, // 2048k - { 0xEF, 0x15, 0x4016, 64, "W25Q32BV" }, // 4096k + { 0xEF, 0x00, 0x4013, 8, "W25Q40BV" }, // 512k + { 0xEF, 0x00, 0x4014, 16, "W25Q80BV" }, // 1024k + { 0xEF, 0x14, 0x4015, 32, "W25Q16BV" }, // 2048k + { 0xEF, 0x15, 0x4016, 64, "W25Q32BV" }, // 4096k - { 0xEF, 0x21, 0x7022, 4, "W25Q02JV" }, - // identified by Manufacturer /Device ID -// { 0xEF, 0x05, 0x0000, 1, "Winbond!!!" }, - { 0xEF, 0x10, 0x0000, 2, "W25*10BV!!!" }, // 128k - { 0xEF, 0x11, 0x0000, 4, "W25*20BV" }, // 256k - { 0xEF, 0x12, 0x0000, 8, "W25*40BV" }, // 512k - { 0xEF, 0x13, 0x0000, 16, "W25*80BV" } // 1024k + { 0xEF, 0x16, 0x7017, 128, "W25Q64JV" }, // 8192k + { 0xEF, 0x21, 0x7022, 4, "W25Q02JV" }, + + // identified by Manufacturer /Device ID only + /// Manufacturer: Renesas + { 0x1F, 0x46, 0x0000, 32, "AT25XE161D" }, // 2048k + { 0x1F, 0x47, 0x0000, 64, "AT25XE321D" }, // 4096k +// { 0xEF, 0x05, 0x0000, 1, "Winbond!!!" }, // 64k (too small !!!) + { 0xEF, 0x10, 0x0000, 2, "W25*10BV!" }, // 128k (small !!!) + { 0xEF, 0x11, 0x0000, 4, "W25*20BV" }, // 256k + { 0xEF, 0x12, 0x0000, 8, "W25*40BV" }, // 512k + { 0xEF, 0x13, 0x0000, 16, "W25*80BV" }, // 1024k + { 0xEF, 0x14, 0x0000, 32, "W25*16*" }, // 2048k + { 0xEF, 0x15, 0x0000, 64, "W25*32*" }, // 4096k + { 0xEF, 0x16, 0x0000, 128, "W25*64*" } // 8192k }; extern uint8_t spi_flash_pages64k; From 23e6aa40b765f93e71a03fa67c3e36e423718d38 Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Thu, 28 Nov 2024 17:33:43 +0100 Subject: [PATCH 15/39] hf/lf tune: fix segfault when called from script --- CHANGELOG.md | 3 ++- client/src/ui.c | 16 ++++++++++------ 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 52e139959..df8ab5448 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,8 @@ All notable changes to this project will be documented in this file. This project uses the changelog in accordance with [keepchangelog](http://keepachangelog.com/). Please use this to write notable changes, which is not the same as git commit log... ## [unreleased][unreleased] -- Add option to set and get maximum read/write block number using `hf_mf_ultimatecard` script (@piotrva) +- Fixed `hf/lf tune` segfault when called from script (@doegox) +- Added option to set and get maximum read/write block number using `hf_mf_ultimatecard` script (@piotrva) - Added JEDEC information for SPI flash W25Q64JV (@ANTodorov) - Added special iclass legacy config cards in `hf iclass configcard` (@antiklesys) - Added simulation function to `hf iclass legrec` (@antiklesys) diff --git a/client/src/ui.c b/client/src/ui.c index 09730d8d3..68cc71a43 100644 --- a/client/src/ui.c +++ b/client/src/ui.c @@ -696,14 +696,18 @@ void print_progress(uint64_t count, uint64_t max, barMode_t style) { max = (count > max) ? count : max; #if defined(HAVE_READLINE) static int prev_cols = 0; - int rows; - rl_reset_screen_size(); // refresh Readline idea of the actual screen width - rl_get_screen_size(&rows, &cols); + int tmp_cols; + rl_get_screen_size(NULL, &tmp_cols); + // if cols==0: impossible to get screen size, e.g. when scripted + if (tmp_cols != 0) { + // don't call it if cols==0, it would segfault + rl_reset_screen_size(); // refresh Readline idea of the actual screen width + rl_get_screen_size(NULL, &cols); - if (cols < 36) - return; + if (cols < 36) + return; + } - (void) rows; if (prev_cols > cols) { PrintAndLogEx(NORMAL, _CLEAR_ _TOP_ ""); } From a3eb3bfbe960bb5cf670fc0613c5091f3e06c2e0 Mon Sep 17 00:00:00 2001 From: Piotr Rzeszut Date: Fri, 29 Nov 2024 01:43:56 +0100 Subject: [PATCH 16/39] Automatically set maximum read/write block by hf_mf_ultimatecard.lua when using preset configurations --- client/luascripts/hf_mf_ultimatecard.lua | 49 ++++++++++++++---------- 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/client/luascripts/hf_mf_ultimatecard.lua b/client/luascripts/hf_mf_ultimatecard.lua index a35b24173..392e9e0d4 100644 --- a/client/luascripts/hf_mf_ultimatecard.lua +++ b/client/luascripts/hf_mf_ultimatecard.lua @@ -644,6 +644,26 @@ local function write_ulm(ulm) return true, 'Ok' end --- +-- Write maximum read/write block number, +local function write_maxRWblk(data) + -- input number check + if data == nil then return nil, 'empty block number' end + if #data == 0 then return nil, 'empty block number' end + if #data ~= 2 then return nil, 'block number wrong length. Should be 1 hex byte' end + + print('Set max R/W block', data) + local info = connect() + if not info then return false, "Can't select card" end + local resp + -- set maximum read/write block + resp = send("CF".._key.."6B"..data) + lib14a.disconnect() + if resp ~= '9000FD07' then return nil, 'Failed to write maximum read/write block' + else + return true, 'Ok' + end +end +--- -- Set type for magic card presets. local function set_type(tagtype) -- tagtype checks @@ -656,6 +676,7 @@ local function set_type(tagtype) send("CF".._key.."F000000000000002000978009102DABC19101011121314151604000900") lib14a.disconnect() write_uid('04112233') + write_maxRWblk('13') -- Setting Mifare mini S20 7-byte elseif tagtype == 2 then print('Setting: Ultimate Magic card to Mifare mini S20 7-byte') @@ -663,6 +684,7 @@ local function set_type(tagtype) send("CF".._key.."F000010000000002000978009102DABC19101011121314151644000900") lib14a.disconnect() write_uid('04112233445566') + write_maxRWblk('13') -- Setting Mifare mini S20 10-byte elseif tagtype == 3 then print('Setting: Ultimate Magic card to Mifare mini S20 10-byte') @@ -670,6 +692,7 @@ local function set_type(tagtype) send("CF".._key.."F000020000000002000978009102DABC19101011121314151684000900") lib14a.disconnect() write_uid('04112233445566778899') + write_maxRWblk('13') -- Setting Mifare 1k S50 4--byte elseif tagtype == 4 then print('Setting: Ultimate Magic card to Mifare 1k S50 4-byte') @@ -677,6 +700,7 @@ local function set_type(tagtype) send("CF".._key.."F000000000000002000978009102DABC19101011121314151604000800") lib14a.disconnect() write_uid('04112233') + write_maxRWblk('3F') -- Setting Mifare 1k S50 7-byte elseif tagtype == 5 then print('Setting: Ultimate Magic card to Mifare 1k S50 7-byte') @@ -684,6 +708,7 @@ local function set_type(tagtype) send("CF".._key.."F000010000000002000978009102DABC19101011121314151644000800") lib14a.disconnect() write_uid('04112233445566') + write_maxRWblk('3F') -- Setting Mifare 1k S50 10-byte elseif tagtype == 6 then print('Setting: Ultimate Magic card to Mifare 1k S50 10-byte') @@ -691,6 +716,7 @@ local function set_type(tagtype) send("CF".._key.."F000020000000002000978009102DABC19101011121314151684000800") lib14a.disconnect() write_uid('04112233445566778899') + write_maxRWblk('3F') -- Setting Mifare 4k S70 4-byte elseif tagtype == 7 then print('Setting: Ultimate Magic card to Mifare 4k S70 4-byte') @@ -698,6 +724,7 @@ local function set_type(tagtype) send("CF".._key.."F000000000000002000978009102DABC19101011121314151602001800") lib14a.disconnect() write_uid('04112233') + write_maxRWblk('FF') -- Setting Mifare 4k S70 7-byte elseif tagtype == 8 then print('Setting: Ultimate Magic card to Mifare 4k S70 7-byte') @@ -705,6 +732,7 @@ local function set_type(tagtype) send("CF".._key.."F000010000000002000978009102DABC19101011121314151642001800") lib14a.disconnect() write_uid('04112233445566') + write_maxRWblk('FF') -- Setting Mifare 4k S70 10-byte elseif tagtype == 9 then print('Setting: Ultimate Magic card to Mifare 4k S70 10-byte') @@ -712,6 +740,7 @@ local function set_type(tagtype) send("CF".._key.."F000020000000002000978009102DABC19101011121314151682001800") lib14a.disconnect() write_uid('04112233445566778899') + write_maxRWblk('FF') -- Setting UL elseif tagtype == 10 then print('Setting: Ultimate Magic card to UL') @@ -1017,26 +1046,6 @@ local function wipe(wtype) end end --- --- Write maximum read/write block number, -local function write_maxRWblk(data) - -- input number check - if data == nil then return nil, 'empty block number' end - if #data == 0 then return nil, 'empty block number' end - if #data ~= 2 then return nil, 'block number wrong length. Should be 1 hex byte' end - - print('Set max R/W block', data) - local info = connect() - if not info then return false, "Can't select card" end - local resp - -- set maximum read/write block - resp = send("CF".._key.."6B"..data) - lib14a.disconnect() - if resp ~= '9000FD07' then return nil, 'Failed to write maximum read/write block' - else - return true, 'Ok' - end -end ---- -- The main entry point function main(args) print() From a11f3173ccd9495d1e260471d888ee3a92761c16 Mon Sep 17 00:00:00 2001 From: Piotr Rzeszut Date: Fri, 29 Nov 2024 01:45:58 +0100 Subject: [PATCH 17/39] Add preset for 2k MIFARE Classic tags hf_mf_ultimatecard.lua when using preset configurations, update changelog --- CHANGELOG.md | 1 + client/luascripts/hf_mf_ultimatecard.lua | 52 +++++++++++++++++------- 2 files changed, 39 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 52e139959..697765632 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ All notable changes to this project will be documented in this file. This project uses the changelog in accordance with [keepchangelog](http://keepachangelog.com/). Please use this to write notable changes, which is not the same as git commit log... ## [unreleased][unreleased] +- Automatically set maximum read/write block when using predefined types in `hf_mf_ultimatecard` script, add 2k predefined types (@piotrva) - Add option to set and get maximum read/write block number using `hf_mf_ultimatecard` script (@piotrva) - Added JEDEC information for SPI flash W25Q64JV (@ANTodorov) - Added special iclass legacy config cards in `hf iclass configcard` (@antiklesys) diff --git a/client/luascripts/hf_mf_ultimatecard.lua b/client/luascripts/hf_mf_ultimatecard.lua index 392e9e0d4..4e65f1942 100644 --- a/client/luascripts/hf_mf_ultimatecard.lua +++ b/client/luascripts/hf_mf_ultimatecard.lua @@ -50,20 +50,20 @@ arguments = [[ -c read magic configuration -u UID (8-20 hexsymbols), set UID on tag -t tag type to impersonate - 1 = Mifare Mini S20 4-byte - 2 = Mifare Mini S20 7-byte | 15 = NTAG 210 - 3 = Mifare Mini S20 10-byte | 16 = NTAG 212 - 4 = Mifare 1k S50 4-byte | 17 = NTAG 213 - 5 = Mifare 1k S50 7-byte | 18 = NTAG 215 - 6 = Mifare 1k S50 10-byte | 19 = NTAG 216 - 7 = Mifare 4k S70 4-byte | 20 = NTAG I2C 1K - 8 = Mifare 4k S70 7-byte | 21 = NTAG I2C 2K - 9 = Mifare 4k S70 10-byte | 22 = NTAG I2C 1K PLUS - *** 10 = UL - NOT WORKING FULLY | 23 = NTAG I2C 2K PLUS - *** 11 = UL-C - NOT WORKING FULLY | 24 = NTAG 213F - 12 = UL EV1 48b | 25 = NTAG 216F - 13 = UL EV1 128b - *** 14 = UL Plus - NOT WORKING YET + 1 = Mifare Mini S20 4-byte | 15 = NTAG 210 + 2 = Mifare Mini S20 7-byte | 16 = NTAG 212 + 3 = Mifare Mini S20 10-byte | 17 = NTAG 213 + 4 = Mifare 1k S50 4-byte | 18 = NTAG 215 + 5 = Mifare 1k S50 7-byte | 19 = NTAG 216 + 6 = Mifare 1k S50 10-byte | 20 = NTAG I2C 1K + 7 = Mifare 4k S70 4-byte | 21 = NTAG I2C 2K + 8 = Mifare 4k S70 7-byte | 22 = NTAG I2C 1K PLUS + 9 = Mifare 4k S70 10-byte | 23 = NTAG I2C 2K PLUS + *** 10 = UL - NOT WORKING FULLY | 24 = NTAG 213F + *** 11 = UL-C - NOT WORKING FULLY | 25 = NTAG 216F + 12 = UL EV1 48b | 26 = Mifare 2k S50 4-byte + 13 = UL EV1 128b | 27 = Mifare 2k S50 7-byte + *** 14 = UL Plus - NOT WORKING YET | 28 = Mifare 2k S50 10-byte -p NTAG password (8 hexsymbols), set NTAG password on tag. -a NTAG pack ( 4 hexsymbols), set NTAG pack on tag. @@ -717,6 +717,30 @@ local function set_type(tagtype) lib14a.disconnect() write_uid('04112233445566778899') write_maxRWblk('3F') + -- Setting Mifare 2k S50 4-byte + elseif tagtype == 26 then + print('Setting: Ultimate Magic card to Mifare 2k S50 4-byte') + connect() + send("CF".._key.."F000000000000002000978009102DABC19101011121314151604000800") + lib14a.disconnect() + write_uid('04112233') + write_maxRWblk('7F') + -- Setting Mifare 2k S50 7-byte + elseif tagtype == 27 then + print('Setting: Ultimate Magic card to Mifare 2k S50 7-byte') + connect() + send("CF".._key.."F000010000000002000978009102DABC19101011121314151644000800") + lib14a.disconnect() + write_uid('04112233445566') + write_maxRWblk('7F') + -- Setting Mifare 2k S50 10-byte + elseif tagtype == 28 then + print('Setting: Ultimate Magic card to Mifare 2k S50 10-byte') + connect() + send("CF".._key.."F000020000000002000978009102DABC19101011121314151684000800") + lib14a.disconnect() + write_uid('04112233445566778899') + write_maxRWblk('7F') -- Setting Mifare 4k S70 4-byte elseif tagtype == 7 then print('Setting: Ultimate Magic card to Mifare 4k S70 4-byte') From e4a3a244ce3c557d1b1e09174c0ca25f709efe3e Mon Sep 17 00:00:00 2001 From: Antiklesys Date: Sat, 30 Nov 2024 07:53:52 +0800 Subject: [PATCH 18/39] Added unhash instructions after legbrute Added unhash instructions after legbrute --- client/src/cmdhficlass.c | 1 + 1 file changed, 1 insertion(+) diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index a7e215e1e..b701aeb19 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -4152,6 +4152,7 @@ static int CmdHFiClassLegRecLookUp(const char *Cmd) { } if (check_values) { PrintAndLogEx(SUCCESS, _GREEN_("CONFIRMED VALID RAW key ") _RED_("%s"), sprint_hex(div_key, 8)); + PrintAndLogEx(INFO, "You can now run -> "_YELLOW_("hf iclass unhash -k %s")" <-to find the pre-images.", sprint_hex(div_key, 8)); verified = true; } else { PrintAndLogEx(INFO, _YELLOW_("Raw Key Invalid")); From ca15bbd01972e36b267543f0cc5897a657c3fe12 Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Thu, 28 Nov 2024 16:59:05 +0100 Subject: [PATCH 19/39] Rework theremin script --- client/pyscripts/theremin.py | 216 +++++++++++++++++++++++++---------- 1 file changed, 156 insertions(+), 60 deletions(-) diff --git a/client/pyscripts/theremin.py b/client/pyscripts/theremin.py index 790fc6bf8..8cb41737a 100755 --- a/client/pyscripts/theremin.py +++ b/client/pyscripts/theremin.py @@ -1,81 +1,177 @@ #!/usr/bin/python3 -### Parameters +import os +import subprocess +import signal +import numpy as np +from pyaudio import PyAudio, paFloat32, paContinue + # Sound output parameters volume = 1.0 -sample_buf_size = 44 -sampling_freq = 44100 #Hz +sampling_freq = 44100 # Hz # Frequency generator parameters -min_freq = 200 #Hz -max_freq = 2000 #Hz +min_freq = 100 # Hz +max_freq = 6000 # Hz # Proxmark3 parameters -pm3_client="/usr/local/bin/proxmark3" -pm3_reader_dev_file="/dev/ttyACM0" -pm3_tune_cmd="hf tune" +pm3_client = "pm3" +pm3_tune_cmd = "hf tune --value" + +frequency = 440 +buffer = [] -### Modules -import numpy -import pyaudio -from select import select -from subprocess import Popen, DEVNULL, PIPE +def find_zero_crossing_index(array): + for i in range(1, len(array)): + if array[i-1] < 0 and array[i] >= 0: + return i + return None # Return None if no zero-crossing is found -### Main program -p = pyaudio.PyAudio() +def generate_sine_wave(frequency, sample_rate, duration, frame_count): + """Generate a sine wave at a given frequency.""" + t = np.linspace(0, duration, int(sample_rate * duration), endpoint=False) + wave = np.sin(2 * np.pi * frequency * t) + return wave[:frame_count] -# For paFloat32 sample values must be in range [-1.0, 1.0] -stream = p.open(format=pyaudio.paFloat32, - channels=1, - rate=sampling_freq, - output=True) -# Initial voltage to frequency values -min_v = 100.0 -max_v = 0.0 -v = 0 -out_freq = min_freq +# PyAudio Callback function +def pyaudio_callback(in_data, frame_count, time_info, status): + # if in_data is None: + # return (in_data, pyaudio.paContinue) + global frequency + global buffer + wave = generate_sine_wave(frequency, sampling_freq, 0.01, frame_count*2) + i = find_zero_crossing_index(buffer) + if i is None: + buffer = wave + else: + buffer = np.concatenate((buffer[:i], wave)) + data = (buffer[:frame_count] * volume).astype(np.float32).tobytes() + buffer = buffer[frame_count:] + return (data, paContinue) +# pyaudio.paComplete -# Spawn the Proxmark3 client -pm3_proc = Popen([pm3_client, pm3_reader_dev_file, "-c", pm3_tune_cmd], bufsize=0, env={}, stdin=DEVNULL, stdout=PIPE, stderr=DEVNULL) -mv_recbuf = "" -# Read voltages from the Proxmark3, generate the sine wave, output to soundcard -sample_buf = [0.0 for x in range(0, sample_buf_size)] -i = 0 -sinev = 0 -while True: +def silent_pyaudio(): + """ + Lifted and adapted from https://stackoverflow.com/questions/67765911/ + PyAudio is noisy af every time you initialise it, which makes reading the + log output rather difficult. The output appears to be being made by the + C internals, so we can't even redirect the logs with Python's logging + facility. Therefore the nuclear option was selected: swallow all stderr + and stdout for the duration of PyAudio's use. + """ - # Read Proxmark3 client's stdout and extract voltage values - if(select([pm3_proc.stdout], [], [], 0)[0]): + # Open a pair of null files + null_fds = [os.open(os.devnull, os.O_RDWR) for x in range(2)] + # Save the actual stdout (1) and stderr (2) file descriptors. + save_fds = [os.dup(1), os.dup(2)] + # Assign the null pointers to stdout and stderr. + os.dup2(null_fds[0], 1) + os.dup2(null_fds[1], 2) + pyaudio = PyAudio() + os.dup2(save_fds[0], 1) + os.dup2(save_fds[1], 2) + # Close all file descriptors + for fd in null_fds + save_fds: + os.close(fd) + return pyaudio - b = pm3_proc.stdout.read(256).decode("ascii") - if "Done" in b: - break; - for c in b: - if c in "0123456789 mV": - mv_recbuf += c - else: - mv_recbuf = "" - if mv_recbuf[-3:] == " mV": - v = int(mv_recbuf[:-3]) / 1000 - if v < min_v: - min_v = v - 0.001 - if v > max_v: - max_v = v + +def run_pm3_cmd(callback): + # Start the process + process = subprocess.Popen( + [pm3_client, '-c', pm3_tune_cmd], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True, + bufsize=1, # Line buffered + shell=False + ) + + # Read the output line by line as it comes + try: + with process.stdout as pipe: + for line in pipe: + # Process each line + l = line.strip() # Strip to remove any extraneous newline characters + callback(l) + except Exception as e: + print(f"An error occurred: {e}") + finally: + # Ensure the subprocess is properly terminated + process.terminate() + process.wait() + + +def linear_to_exponential_freq(v, min_v, max_v, min_freq, max_freq): + # First, map v to a range between 0 and 1 + if max_v != min_v: + normalized_v = (v - min_v) / (max_v - min_v) + else: + normalized_v = 0.5 + normalized_v = 1 - normalized_v + + # Calculate the ratio of the max frequency to the min frequency + freq_ratio = max_freq / min_freq + + # Calculate the exponential frequency using the mapped v + freq = min_freq * (freq_ratio ** normalized_v) + return freq + + +class foo(): + def __init__(self): + self.p = silent_pyaudio() + # For paFloat32 sample values must be in range [-1.0, 1.0] + self.stream = self.p.open(format=paFloat32, + channels=1, + rate=sampling_freq, + output=True, + stream_callback=pyaudio_callback) + + # Initial voltage to frequency values + self.min_v = 50000.0 + self.max_v = 0.0 + + # Setting the signal handler for SIGINT (Ctrl+C) + signal.signal(signal.SIGINT, self.signal_handler) + + # Start the stream + self.stream.start_stream() + + def __exit__(self): + self.stream.stop_stream() + self.stream.close() + self.p.terminate() + + def signal_handler(self, sig, frame): + print("\nYou pressed Ctrl+C! Press Enter") + self.__exit__() + + def callback(self, line): + if 'mV' not in line: + return + v = int(line.split(' ')[1]) + if v == 0: + return + self.min_v = min(self.min_v, v) + self.max_v = max(self.max_v, v) # Recalculate the audio frequency to generate - out_freq = (max_freq - min_freq) * (max_v - v) / (max_v - min_v) \ - + min_freq + global frequency + frequency = linear_to_exponential_freq(v, self.min_v, self.max_v, min_freq, max_freq) - # Generate the samples and write them to the soundcard - sinevs = out_freq / sampling_freq * numpy.pi * 2 - sample_buf[i] = sinev - sinev += sinevs - sinev = sinev if sinev < numpy.pi * 2 else sinev - numpy.pi * 2 - i = (i + 1) % sample_buf_size - if not i: - stream.write((numpy.sin(sample_buf) * volume). - astype(numpy.float32).tobytes()) +# frequency = max_freq - ((max_freq - min_freq) * (v - self.min_v) / (self.max_v - self.min_v) + min_freq) + #frequency = (frequency + new_frequency)/2 + + +def main(): + f = foo() + run_pm3_cmd(f.callback) + + +if __name__ == "__main__": + main() From d8d090612d00248c61fc3bd6b4f4185dcdc6269b Mon Sep 17 00:00:00 2001 From: Lucifer Voeltner Date: Sat, 30 Nov 2024 17:57:48 +0700 Subject: [PATCH 20/39] 'hf mfu incr' to increment counters of UL-EV1 family; Also fix reading of NTAG counters in 'hf mfu info' --- client/src/cmdhfmfu.c | 132 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 123 insertions(+), 9 deletions(-) diff --git a/client/src/cmdhfmfu.c b/client/src/cmdhfmfu.c index 658e028e5..68869a6fb 100644 --- a/client/src/cmdhfmfu.c +++ b/client/src/cmdhfmfu.c @@ -1540,21 +1540,13 @@ static int ulev1_print_version(uint8_t *data) { } static int ntag_print_counter(void) { - // NTAG has one counter/tearing. At address 0x02. + // NTAG has one counter. At address 0x02. With no tearing. PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "--- " _CYAN_("Tag Counter")); - uint8_t tear[1] = {0}; uint8_t counter[3] = {0, 0, 0}; uint16_t len; - len = ulev1_readTearing(0x02, tear, sizeof(tear)); - (void)len; len = ulev1_readCounter(0x02, counter, sizeof(counter)); - (void)len; PrintAndLogEx(INFO, " [02]: %s", sprint_hex(counter, 3)); - PrintAndLogEx(SUCCESS, " - %02X tearing ( %s )" - , tear[0] - , (tear[0] == 0xBD) ? _GREEN_("ok") : _RED_("fail") - ); return len; } @@ -5833,6 +5825,127 @@ out: return PM3_SUCCESS; } +static int CmdHF14AMfUIncr(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mfu incr", + "Increment a MIFARE Ultralight Ev1 counter\n" + "Will read but not increment counter if NTAG is detected", + "hf mfu incr -c 0 -v 1337\n" + "hf mfu incr -c 2 -v 0 -p FFFFFFFF"); + void *argtable[] = { + arg_param_begin, + arg_int1("c", "cnt", "", "Counter index from 0"), + arg_int1("v", "val", "", "Value to increment by (0-16777215)"), + arg_str0("p", "pwd", "", "PWD to authenticate with"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + uint8_t counter = arg_get_int_def(ctx, 1, 3); + uint32_t value = arg_get_u32_def(ctx, 2, 16777216); + + int pwd_len; + uint8_t pwd[4] = { 0x00 }; + CLIGetHexWithReturn(ctx, 3, pwd, &pwd_len); + bool has_key = false; + if (pwd_len) { + has_key = true; + if (pwd_len != 4) { + PrintAndLogEx(WARNING, "incorrect PWD length"); + return PM3_EINVARG; + } + } + + CLIParserFree(ctx); + + if (counter > 2) { + PrintAndLogEx(WARNING, "Counter index must be in range 0-2"); + return PM3_EINVARG; + } + if (value > 16777215) { + PrintAndLogEx(WARNING, "Value to increment must be in range 0-16777215"); + return PM3_EINVARG; + } + + uint8_t increment_cmd[6] = { MIFARE_ULEV1_INCR_CNT, counter, 0x00, 0x00, 0x00, 0x00 }; + + for (uint8_t i = 0; i < 3; i++) { + increment_cmd[i + 2] = (value >> (8 * i)) & 0xff; + } + + iso14a_card_select_t card; + if (ul_select(&card) == false) { + PrintAndLogEx(FAILED, "failed to select card, exiting..."); + return PM3_ESOFT; + } + + uint64_t tagtype = GetHF14AMfU_Type(); + uint64_t tags_with_counter_ul = MFU_TT_UL_EV1_48 | MFU_TT_UL_EV1_128 | MFU_TT_UL_EV1; + uint64_t tags_with_counter_ntag = MFU_TT_NTAG_213 | MFU_TT_NTAG_213_F | MFU_TT_NTAG_213_C | MFU_TT_NTAG_213_TT | MFU_TT_NTAG_215 | MFU_TT_NTAG_216; + if ((tagtype & (tags_with_counter_ul | tags_with_counter_ntag)) == 0) { + PrintAndLogEx(WARNING, "tag type does not have counters"); + DropField(); + return PM3_ESOFT; + } + + bool is_ntag = (tagtype & tags_with_counter_ntag) != 0; + if (is_ntag && (counter != 2)) { + PrintAndLogEx(WARNING, "NTAG only has one counter at index 2"); + DropField(); + return PM3_EINVARG; + } + + uint8_t pack[4] = { 0, 0, 0, 0 }; + if (has_key) { + if (ulev1_requestAuthentication(pwd, pack, sizeof(pack)) == PM3_EWRONGANSWER) { + PrintAndLogEx(FAILED, "authentication failed UL-EV1/NTAG"); + DropField(); + return PM3_ESOFT; + } + } + + uint8_t current_counter[3] = { 0, 0, 0 }; + int len = ulev1_readCounter(counter, current_counter, sizeof(current_counter)); + if (len != sizeof(current_counter)) { + PrintAndLogEx(FAILED, "failed to read old counter"); + if (is_ntag) { + PrintAndLogEx(HINT, "NTAG detected, try reading with PWD"); + } + DropField(); + return PM3_ESOFT; + } + + uint32_t current_counter_num = current_counter[0] | (current_counter[1] << 8) | (current_counter[2] << 16); + PrintAndLogEx(INFO, "Current counter... " _GREEN_("%8d") " - " _GREEN_("%s"), current_counter_num, sprint_hex(current_counter, 3)); + + if ((tagtype & tags_with_counter_ntag) != 0) { + PrintAndLogEx(WARNING, "NTAG detected, unable to manually increment counter"); + DropField(); + return PM3_ESOFT; + } + + uint8_t resp[1] = { 0x00 }; + if (ul_send_cmd_raw(increment_cmd, sizeof(increment_cmd), resp, sizeof(resp)) < 0) { + PrintAndLogEx(FAILED, "failed to increment counter"); + DropField(); + return PM3_ESOFT; + } + + uint8_t new_counter[3] = { 0, 0, 0 }; + int new_len = ulev1_readCounter(counter, new_counter, sizeof(new_counter)); + if (new_len != sizeof(current_counter)) { + PrintAndLogEx(FAILED, "failed to read new counter"); + DropField(); + return PM3_ESOFT; + } + + uint32_t new_counter_num = new_counter[0] | (new_counter[1] << 8) | (new_counter[2] << 16); + PrintAndLogEx(INFO, "New counter....... " _GREEN_("%8d") " - " _GREEN_("%s"), new_counter_num, sprint_hex(new_counter, 3)); + + DropField(); + return PM3_SUCCESS; +} + static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help"}, {"list", CmdHF14AMfuList, AlwaysAvailable, "List MIFARE Ultralight / NTAG history"}, @@ -5845,6 +5958,7 @@ static command_t CommandTable[] = { {"cauth", CmdHF14AMfUCAuth, IfPm3Iso14443a, "Ultralight-C - Authentication"}, {"setpwd", CmdHF14AMfUCSetPwd, IfPm3Iso14443a, "Ultralight-C - Set 3DES key"}, {"dump", CmdHF14AMfUDump, IfPm3Iso14443a, "Dump MIFARE Ultralight family tag to binary file"}, + {"incr", CmdHF14AMfUIncr, IfPm3Iso14443a, "Increments Ev1/NTAG counter"}, {"info", CmdHF14AMfUInfo, IfPm3Iso14443a, "Tag information"}, {"ndefread", CmdHF14MfuNDEFRead, IfPm3Iso14443a, "Prints NDEF records from card"}, {"rdbl", CmdHF14AMfURdBl, IfPm3Iso14443a, "Read block"}, From 4adf6633004ac6efdbd42715e1d7131605d028cc Mon Sep 17 00:00:00 2001 From: Lucifer Voeltner Date: Sun, 1 Dec 2024 16:27:35 +0700 Subject: [PATCH 21/39] Fix Gen 3 APDU block 0 SAK not being written correctly --- armsrc/mifarecmd.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/armsrc/mifarecmd.c b/armsrc/mifarecmd.c index 659c5eec0..2eed2ca5c 100644 --- a/armsrc/mifarecmd.c +++ b/armsrc/mifarecmd.c @@ -3482,7 +3482,8 @@ void MifareGen3Blk(uint8_t block_len, uint8_t *block) { retval = PM3_ESOFT; goto OUT; } - cmd[ofs++] = card_info->sak; + cmd[ofs] = block_len < card_info->uidlen ? card_info->sak : cmd[ofs]; + ofs++; cmd[ofs++] = card_info->atqa[0]; cmd[ofs++] = card_info->atqa[1]; AddCrc14A(cmd, sizeof(block_cmd) + MIFARE_BLOCK_SIZE); From ef74de37218776cbab4d7a4fa682c605feac3713 Mon Sep 17 00:00:00 2001 From: Xavier <90627943+kitsunehunter@users.noreply.github.com> Date: Sun, 1 Dec 2024 20:17:05 -0500 Subject: [PATCH 22/39] add static laundry card keys these cards are stored value and manipulating the data is useful Signed-off-by: Xavier <90627943+kitsunehunter@users.noreply.github.com> --- client/dictionaries/mfc_default_keys.dic | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/client/dictionaries/mfc_default_keys.dic b/client/dictionaries/mfc_default_keys.dic index 76b5198b6..646099bad 100644 --- a/client/dictionaries/mfc_default_keys.dic +++ b/client/dictionaries/mfc_default_keys.dic @@ -2760,3 +2760,9 @@ D37C8F1793F7 56cf3acd90ca 542089792be2 5420aeada758 +#Coinamatic laundry +0734BFB93DAB +85A438F72A8A +#CSC Laundry +212223242555 +717273747555 From 4e40d3d6a732216bbff90de577357a2f9955e3dd Mon Sep 17 00:00:00 2001 From: Xavier <90627943+kitsunehunter@users.noreply.github.com> Date: Sun, 1 Dec 2024 20:27:08 -0500 Subject: [PATCH 23/39] Update mfc_default_keys.dic Signed-off-by: Xavier <90627943+kitsunehunter@users.noreply.github.com> --- client/dictionaries/mfc_default_keys.dic | 3 --- 1 file changed, 3 deletions(-) diff --git a/client/dictionaries/mfc_default_keys.dic b/client/dictionaries/mfc_default_keys.dic index 646099bad..f3a339c93 100644 --- a/client/dictionaries/mfc_default_keys.dic +++ b/client/dictionaries/mfc_default_keys.dic @@ -2760,9 +2760,6 @@ D37C8F1793F7 56cf3acd90ca 542089792be2 5420aeada758 -#Coinamatic laundry -0734BFB93DAB -85A438F72A8A #CSC Laundry 212223242555 717273747555 From 96a1f21764e239551a21e7f1ced3a344969ae64e Mon Sep 17 00:00:00 2001 From: nvx Date: Mon, 2 Dec 2024 16:16:59 +1000 Subject: [PATCH 24/39] fix pacs data in example trace filename --- ...40001.trace => hf_seos_sniff_fc54_cn64001.trace} | Bin 1 file changed, 0 insertions(+), 0 deletions(-) rename traces/{hf_seos_sniff_fc60_cn640001.trace => hf_seos_sniff_fc54_cn64001.trace} (100%) diff --git a/traces/hf_seos_sniff_fc60_cn640001.trace b/traces/hf_seos_sniff_fc54_cn64001.trace similarity index 100% rename from traces/hf_seos_sniff_fc60_cn640001.trace rename to traces/hf_seos_sniff_fc54_cn64001.trace From 7fbcb1c30f869c903ade1a2dcd70ec2ae7e3e499 Mon Sep 17 00:00:00 2001 From: nya0 Date: Mon, 2 Dec 2024 16:15:13 +0300 Subject: [PATCH 25/39] added "lf hitag hts dump" command --- client/src/cmdlfhitaghts.c | 91 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) diff --git a/client/src/cmdlfhitaghts.c b/client/src/cmdlfhitaghts.c index d1af178dd..933427ec8 100644 --- a/client/src/cmdlfhitaghts.c +++ b/client/src/cmdlfhitaghts.c @@ -444,6 +444,96 @@ static int CmdLFHitagSRead(const char *Cmd) { return PM3_SUCCESS; } +static int CmdLFHitagSDump(const char *Cmd) { + + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf hitag hts dump", + "Read all Hitag S memory and save to file\n" + " Crypto mode: \n" + " - key format ISK high + ISK low\n" + " - default key 4F4E4D494B52 (ONMIKR)\n\n" + " 8268/8310 password mode: \n" + " - default password BBDD3399\n", + "lf hitag hts dump --82xx -k BBDD3399 -> pwd mode\n" + "lf hitag hts dump --crypto -> use def crypto\n" + "lf hitag hts dump -k 4F4E4D494B52 -> crypto mode\n" + "lf hitag hts dump --nrar 0102030411223344\n" + ); + + void *argtable[] = { + arg_param_begin, + arg_lit0("8", "82xx", "8268/8310 mode"), + arg_str0(NULL, "nrar", "", "nonce / answer writer, 8 hex bytes"), + arg_lit0(NULL, "crypto", "crypto mode"), + arg_str0("k", "key", "", "pwd or key, 4 or 6 hex bytes"), + arg_int0("m", "mode", "", "response protocol mode. 0 (Standard 00110), 1 (Advanced 11000), 2 (Advanced 11001), 3 (Fast Advanced 11010) (def: 3)"), + arg_str0("f", "file", "", "specify file name"), + arg_lit0(NULL, "ns", "no save to file"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + lf_hitag_data_t packet; + memset(&packet, 0, sizeof(packet)); + + if (process_hitags_common_args(ctx, &packet) < 0) { + CLIParserFree(ctx); + return PM3_EINVARG; + } + + int fnlen = 0; + char filename[FILE_PATH_SIZE] = {0}; + CLIParamStrToBuf(arg_get_str(ctx, 6), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); + + bool nosave = arg_get_lit(ctx, 7); + CLIParserFree(ctx); + + // read all pages + packet.page = 0; + packet.page_count = 0; + + clearCommandBuffer(); + SendCommandNG(CMD_LF_HITAGS_READ, (uint8_t *) &packet, sizeof(packet)); + + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_LF_HITAGS_READ, &resp, 5000) == false) { + PrintAndLogEx(WARNING, "timeout while waiting for reply."); + return PM3_ETIMEOUT; + } + + if (resp.status != PM3_SUCCESS) { + print_error(resp.reason); + return PM3_ESOFT; + } + + lf_hts_read_response_t *card = (lf_hts_read_response_t *)resp.data.asBytes; + + const int hts_mem_sizes[] = {1, 8, 64, 64}; + int mem_size = hts_mem_sizes[card->config_page.s.MEMT] * HITAGS_PAGE_SIZE; + + hitags_config_t config = card->config_page.s; + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "--- " _CYAN_("Tag Information") " ---------------------------"); + hitags_config_print(config); + + if (nosave) { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "Called with no save option"); + PrintAndLogEx(NORMAL, ""); + return PM3_SUCCESS; + } + + if (fnlen < 1) { + char *fptr = filename; + fptr += snprintf(filename, sizeof(filename), "lf-hitags-"); + FillFileNameByUID(fptr, card->pages[HITAGS_UID_PADR], "-dump", HITAGS_PAGE_SIZE); + } + + pm3_save_dump(filename, (uint8_t *)card->pages, mem_size, jsfHitag); + + return PM3_SUCCESS; +} + static int CmdLFHitagSWrite(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "lf hitag hts wrbl", @@ -615,6 +705,7 @@ static command_t CommandTable[] = { {"-----------", CmdHelp, IfPm3Hitag, "----------------------- " _CYAN_("General") " ------------------------"}, {"reader", CmdLFHitagSReader, IfPm3Hitag, "Act like a Hitag S reader"}, {"rdbl", CmdLFHitagSRead, IfPm3Hitag, "Read Hitag S page"}, + {"dump", CmdLFHitagSDump, IfPm3Hitag, "Dump Hitag S pages to a file"}, {"wrbl", CmdLFHitagSWrite, IfPm3Hitag, "Write Hitag S page"}, {"-----------", CmdHelp, IfPm3Hitag, "----------------------- " _CYAN_("Simulation") " -----------------------"}, {"sim", CmdLFHitagSSim, IfPm3Hitag, "Simulate Hitag S transponder"}, From 416770c17052d39ad46688ab2f907a3550228c4e Mon Sep 17 00:00:00 2001 From: Piotr Rzeszut Date: Mon, 2 Dec 2024 22:43:05 +0100 Subject: [PATCH 26/39] Remove support for 2k MIFARE tags, as it will be better to add this together with other variants of MIFARE Plus in the future --- client/luascripts/hf_mf_ultimatecard.lua | 30 +++--------------------- 1 file changed, 3 insertions(+), 27 deletions(-) diff --git a/client/luascripts/hf_mf_ultimatecard.lua b/client/luascripts/hf_mf_ultimatecard.lua index 4e65f1942..e3529e366 100644 --- a/client/luascripts/hf_mf_ultimatecard.lua +++ b/client/luascripts/hf_mf_ultimatecard.lua @@ -61,9 +61,9 @@ arguments = [[ 9 = Mifare 4k S70 10-byte | 23 = NTAG I2C 2K PLUS *** 10 = UL - NOT WORKING FULLY | 24 = NTAG 213F *** 11 = UL-C - NOT WORKING FULLY | 25 = NTAG 216F - 12 = UL EV1 48b | 26 = Mifare 2k S50 4-byte - 13 = UL EV1 128b | 27 = Mifare 2k S50 7-byte - *** 14 = UL Plus - NOT WORKING YET | 28 = Mifare 2k S50 10-byte + 12 = UL EV1 48b | + 13 = UL EV1 128b | + *** 14 = UL Plus - NOT WORKING YET | -p NTAG password (8 hexsymbols), set NTAG password on tag. -a NTAG pack ( 4 hexsymbols), set NTAG pack on tag. @@ -717,30 +717,6 @@ local function set_type(tagtype) lib14a.disconnect() write_uid('04112233445566778899') write_maxRWblk('3F') - -- Setting Mifare 2k S50 4-byte - elseif tagtype == 26 then - print('Setting: Ultimate Magic card to Mifare 2k S50 4-byte') - connect() - send("CF".._key.."F000000000000002000978009102DABC19101011121314151604000800") - lib14a.disconnect() - write_uid('04112233') - write_maxRWblk('7F') - -- Setting Mifare 2k S50 7-byte - elseif tagtype == 27 then - print('Setting: Ultimate Magic card to Mifare 2k S50 7-byte') - connect() - send("CF".._key.."F000010000000002000978009102DABC19101011121314151644000800") - lib14a.disconnect() - write_uid('04112233445566') - write_maxRWblk('7F') - -- Setting Mifare 2k S50 10-byte - elseif tagtype == 28 then - print('Setting: Ultimate Magic card to Mifare 2k S50 10-byte') - connect() - send("CF".._key.."F000020000000002000978009102DABC19101011121314151684000800") - lib14a.disconnect() - write_uid('04112233445566778899') - write_maxRWblk('7F') -- Setting Mifare 4k S70 4-byte elseif tagtype == 7 then print('Setting: Ultimate Magic card to Mifare 4k S70 4-byte') From cbc7d61781173ea6d79dae796cb8197dea3b6e29 Mon Sep 17 00:00:00 2001 From: Lucifer Voeltner Date: Tue, 3 Dec 2024 10:13:09 +0700 Subject: [PATCH 27/39] modify 'hf mf gen3blk' help to comply with the sak change --- client/src/cmdhfmf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/cmdhfmf.c b/client/src/cmdhfmf.c index 2bfd6a020..877bbdefb 100644 --- a/client/src/cmdhfmf.c +++ b/client/src/cmdhfmf.c @@ -7202,7 +7202,7 @@ static int CmdHf14AGen3Block(const char *Cmd) { " - You can specify part of manufacturer block as\n" " 4/7-bytes for UID change only\n" "\n" - "NOTE: BCC, SAK, ATQA will be calculated automatically" + "NOTE: BCC and ATQA will be calculated automatically" , "hf mf gen3blk --> print current data\n" "hf mf gen3blk -d 01020304 --> set 4 byte uid\n" From e416080ae8e44a8328910962374cad7d4349044f Mon Sep 17 00:00:00 2001 From: Lucifer Voeltner Date: Tue, 3 Dec 2024 10:23:41 +0700 Subject: [PATCH 28/39] make the help message even clearer, and fix a bug featuring me being unable to count --- armsrc/mifarecmd.c | 2 +- client/src/cmdhfmf.c | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/armsrc/mifarecmd.c b/armsrc/mifarecmd.c index 2eed2ca5c..9820d19a2 100644 --- a/armsrc/mifarecmd.c +++ b/armsrc/mifarecmd.c @@ -3482,7 +3482,7 @@ void MifareGen3Blk(uint8_t block_len, uint8_t *block) { retval = PM3_ESOFT; goto OUT; } - cmd[ofs] = block_len < card_info->uidlen ? card_info->sak : cmd[ofs]; + cmd[ofs] = block_len <= card_info->uidlen ? card_info->sak : cmd[ofs]; ofs++; cmd[ofs++] = card_info->atqa[0]; cmd[ofs++] = card_info->atqa[1]; diff --git a/client/src/cmdhfmf.c b/client/src/cmdhfmf.c index 877bbdefb..e61a6a7e9 100644 --- a/client/src/cmdhfmf.c +++ b/client/src/cmdhfmf.c @@ -7202,7 +7202,8 @@ static int CmdHf14AGen3Block(const char *Cmd) { " - You can specify part of manufacturer block as\n" " 4/7-bytes for UID change only\n" "\n" - "NOTE: BCC and ATQA will be calculated automatically" + "NOTE: BCC and ATQA will be calculated automatically\n" + "SAK will be automatically set to default values if not specified" , "hf mf gen3blk --> print current data\n" "hf mf gen3blk -d 01020304 --> set 4 byte uid\n" From bd803ce8fdaa4f3bcc94483ce4d0368656c519ca Mon Sep 17 00:00:00 2001 From: ANTodorov Date: Sat, 30 Nov 2024 14:38:13 +0200 Subject: [PATCH 29/39] rework to use smart SPI flash detection Check JEDEC ID is in range between 0x0001 ... 0xFFFE, Compare the output from 0x90 and 0x9F, Then the size from the JEDEC ID Otherwise fall-back to 256 kB Extend the spi_flash_decode.py to handle more (known) SPI flash ICs --- CHANGELOG.md | 1 + client/pyscripts/spi_flash_decode.py | 158 ++++++++++++++++++--------- common_arm/flashmem.c | 21 +--- common_arm/flashmem.h | 33 ------ 4 files changed, 114 insertions(+), 99 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 52e139959..87968ef48 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ All notable changes to this project will be documented in this file. This project uses the changelog in accordance with [keepchangelog](http://keepachangelog.com/). Please use this to write notable changes, which is not the same as git commit log... ## [unreleased][unreleased] +- Changed SPI flash detection to calculate the size instead of table lookup, updated spi_flash_decode.py script with more ICs (@ANTodorov) - Add option to set and get maximum read/write block number using `hf_mf_ultimatecard` script (@piotrva) - Added JEDEC information for SPI flash W25Q64JV (@ANTodorov) - Added special iclass legacy config cards in `hf iclass configcard` (@antiklesys) diff --git a/client/pyscripts/spi_flash_decode.py b/client/pyscripts/spi_flash_decode.py index 0f125844b..2a89f57af 100644 --- a/client/pyscripts/spi_flash_decode.py +++ b/client/pyscripts/spi_flash_decode.py @@ -12,62 +12,118 @@ except ModuleNotFoundError: return str(s) spi = { + 0x68:{ + "manufacturer": "Boya", + "jedec" : { + 0x40: { + 0x15: { + "part": "BY25Q16BS", + "size": "16mbits", + "sizeB": "2MB", + }, + }, + }, + }, 0x85:{ "manufacturer": "Puya", - 0x60: { - 0x15: { - "part": "P25Q16H", - "size": "16mbits", - "sizeB": "2MB", + "jedec" : { + 0x60: { + 0x15: { + "part": "P25Q16H", + "size": "16mbits", + "sizeB": "2MB", + }, + 0x16: { + "part": "P25Q32H", + "size": "32mbits", + "sizeB": "4MB", + }, + 0x17: { + "part": "P25Q64H", + "size": "64mbits", + "sizeB": "8MB", + }, }, }, }, 0xEF:{ "manufacturer": "Winbond", - 0x30: { - 0x11: { - "part": "W25X10BV", - "size": "1mbits", - "sizeB": "128KB", + "jedec" : { + 0x30: { + 0x11: { + "part": "W25X10BV", + "size": "1mbits", + "sizeB": "128KB", + }, + 0x12: { + "part": "W25X20BV", + "size": "2mbits", + "sizeB": "256KB", + }, + 0x13: { + "part": "W25X40BV", + "size": "4mbits", + "sizeB": "512KB", + }, }, - 0x12: { - "part": "W25X20BV", - "size": "2mbits", - "sizeB": "256KB", + 0x40: { + 0x12: { + "part": "W25Q20BV", + "size": "2mbits", + "sizeB": "256KB", + }, + 0x13: { + "part": "W25Q40BV", + "size": "4mbits", + "sizeB": "512KB", + }, + 0x14: { + "part": "W25Q80BV", + "size": "8mbits", + "sizeB": "1MB", + }, + 0x15: { + "part": "W25Q16BV", + "size": "16mbits", + "sizeB": "2MB", + }, + 0x16: { + "part": "W25Q32BV", + "size": "32mbits", + "sizeB": "4MB", + }, + 0x17: { + "part": "W25Q64BV", + "size": "64mbits", + "sizeB": "8MB", + }, }, - 0x13: { - "part": "W25X40BV", - "size": "4mbits", - "sizeB": "512KB", - }, - }, - 0x40: { - 0x13: { - "part": "W25Q40BV", - "size": "4mbits", - "sizeB": "512KB", - }, - 0x14: { - "part": "W25Q80BV", - "size": "8mbits", - "sizeB": "1MB", - }, - 0x15: { - "part": "W25Q16BV", - "size": "16mbits", - "sizeB": "2MB", - }, - 0x16: { - "part": "W25Q32BV", - "size": "32mbits", - "sizeB": "4MB", - }, - }, - 0x70: { - 0x22: { - "part": "W25Q02JV-IM", - "size": "2mbits", - "sizeB": "256KB", + 0x70: { + 0x14: { + "part": "W25Q80JV", + "size": "8mbits", + "sizeB": "1MB", + }, + 0x15: { + "part": "W25Q16JV", + "size": "16mbits", + "sizeB": "2MB", + }, + 0x16: { + "part": "W25Q32JV", + "size": "32mbits", + "sizeB": "4MB", + }, + 0x17: { + "part": "W25Q64JV", + "size": "64mbits", + "sizeB": "8MB", + }, + 0x22: { + "part": "W25Q02JV-IM", + "size": "2mbits", + "sizeB": "256KB", + }, }, }, }, @@ -90,16 +146,16 @@ for line in p.grabbed_output.split('\n'): did_h = did >> 8 did_l = did & 0xff t = None - + print(f"\n JEDEC ID....... 0x{mid:X} / 0x{did:X}") if mid in spi: mfr = spi[mid]['manufacturer'] - if did_h in spi[mid]: + if did_h in spi[mid]['jedec']: - if did_l in spi[mid][did_h]: + if did_l in spi[mid]['jedec'][did_h]: - t = spi[mid][did_h][did_l] + t = spi[mid]['jedec'][did_h][did_l] print("\n Manufacturer... " + color(f"{mfr}", fg="green") + "\n Device......... " + color(f"{t['part']}", fg="green") + "\n Size........... " + color(f"{t['size']} ({t['sizeB']})", fg="yellow") diff --git a/common_arm/flashmem.c b/common_arm/flashmem.c index 5aa88b0eb..76b475e9c 100644 --- a/common_arm/flashmem.c +++ b/common_arm/flashmem.c @@ -366,7 +366,6 @@ void Flashmem_print_status(void) { ); } - Dbprintf(" Device.................. " _YELLOW_("%s"), spi_flash_data.device); Dbprintf(" Memory size............. " _YELLOW_("%d kB (%d pages * 64k)"), spi_flash_pages64k * 64, spi_flash_pages64k); uint8_t uid[8] = {0, 0, 0, 0, 0, 0, 0, 0}; @@ -442,21 +441,13 @@ bool FlashDetect(void) { } else { if (g_dbglevel > 3) Dbprintf("Flash_ReadID failed reading Mfr/Dev (0x90)"); } - // default device is 'unknown' - spi_flash_data.device = SpiFlashTable[0].device; - + // Check JEDEC data is valid, compare the reported device types and then calculate the number of pages + // It is covering the most (known) cases of devices but probably there are vendors with different data + // They will be handled when there is such cases if (ret) { - for (int i = 0; i < ARRAYLEN(SpiFlashTable); i++) { - if (SpiFlashTable[i].manufacturer_id == spi_flash_data.manufacturer_id) { - if (SpiFlashTable[i].jedec_id == spi_flash_data.jedec_id) { - spi_flash_pages64k = SpiFlashTable[i].pages64k; - spi_flash_data.device = SpiFlashTable[i].device; - break; - } - if (SpiFlashTable[i].device_id == spi_flash_data.device_id) { - spi_flash_data.device = SpiFlashTable[i].device; - break; - } + if (spi_flash_data.jedec_id > 0 && spi_flash_data.jedec_id < 0xFFFF) { + if (((spi_flash_data.device_id + 1) & 0x0F) == (spi_flash_data.jedec_id & 0x000F)) { + spi_flash_pages64k = 1 << (spi_flash_data.jedec_id & 0x000F); } } } diff --git a/common_arm/flashmem.h b/common_arm/flashmem.h index 616575e49..f20dd0bff 100644 --- a/common_arm/flashmem.h +++ b/common_arm/flashmem.h @@ -140,41 +140,8 @@ typedef struct { uint8_t manufacturer_id; uint8_t device_id; uint16_t jedec_id; - uint8_t pages64k; - char *device; } spi_flash_t; -static const spi_flash_t SpiFlashTable[] = { - // first element is the default of 4 * 64kB pages (256kB) - { 0x00, 0x00, 0x0000, 4, "unknown" }, // 256k - // Manufacturer: Puya - { 0x85, 0x14, 0x6015, 32, "P25Q16H" }, // 2048k - // Manufacturer: Winbond - { 0xEF, 0x00, 0x3012, 4, "W25X20BV" }, // 256k - { 0xEF, 0x00, 0x3013, 8, "W25X40BV" }, // 512k - - { 0xEF, 0x00, 0x4013, 8, "W25Q40BV" }, // 512k - { 0xEF, 0x00, 0x4014, 16, "W25Q80BV" }, // 1024k - { 0xEF, 0x14, 0x4015, 32, "W25Q16BV" }, // 2048k - { 0xEF, 0x15, 0x4016, 64, "W25Q32BV" }, // 4096k - - { 0xEF, 0x16, 0x7017, 128, "W25Q64JV" }, // 8192k - { 0xEF, 0x21, 0x7022, 4, "W25Q02JV" }, - - // identified by Manufacturer /Device ID only - /// Manufacturer: Renesas - { 0x1F, 0x46, 0x0000, 32, "AT25XE161D" }, // 2048k - { 0x1F, 0x47, 0x0000, 64, "AT25XE321D" }, // 4096k -// { 0xEF, 0x05, 0x0000, 1, "Winbond!!!" }, // 64k (too small !!!) - { 0xEF, 0x10, 0x0000, 2, "W25*10BV!" }, // 128k (small !!!) - { 0xEF, 0x11, 0x0000, 4, "W25*20BV" }, // 256k - { 0xEF, 0x12, 0x0000, 8, "W25*40BV" }, // 512k - { 0xEF, 0x13, 0x0000, 16, "W25*80BV" }, // 1024k - { 0xEF, 0x14, 0x0000, 32, "W25*16*" }, // 2048k - { 0xEF, 0x15, 0x0000, 64, "W25*32*" }, // 4096k - { 0xEF, 0x16, 0x0000, 128, "W25*64*" } // 8192k -}; - extern uint8_t spi_flash_pages64k; bool FlashDetect(void); From 2950d7870317eb4924fb329b98ad2db7ee2c02ff Mon Sep 17 00:00:00 2001 From: ry4000 <154689120+ry4000@users.noreply.github.com> Date: Tue, 3 Dec 2024 22:51:29 +1100 Subject: [PATCH 30/39] R&Y: Updated DEN MyRide AID in aid_desfire.json - Updated Vendor to its legal name (Masabi Ltd) - Added a standardised description to identify the AID as being issued by Masabi Ltd for its Justride platform. Thank you. -R&Y. Signed-off-by: ry4000 <154689120+ry4000@users.noreply.github.com> --- client/resources/aid_desfire.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/resources/aid_desfire.json b/client/resources/aid_desfire.json index 53cb47992..8ecd18a6e 100644 --- a/client/resources/aid_desfire.json +++ b/client/resources/aid_desfire.json @@ -1065,10 +1065,10 @@ }, { "AID": "DD00DD", - "Vendor": "Regional Transporation District (RTD) via masabi justride", + "Vendor": "Regional Transporation District (RTD) via Masabi Ltd", "Country": "US", "Name": "MyRide Card (DEN)", - "Description": "DEN MyRide Card", + "Description": "DEN MyRide Card; Masabi Justride Tap and Ride DESFire Smartcard", "Type": "transport" }, { From 537a9f0171ed9fdafd52d1ba7a98fcf098da836b Mon Sep 17 00:00:00 2001 From: libin-ka <46210417+libin-ka@users.noreply.github.com> Date: Tue, 3 Dec 2024 21:04:50 +0800 Subject: [PATCH 31/39] Add files via upload Add Proxmark3 Ultimate FPGA xc2s50-5-tq144.ucf files Signed-off-by: libin-ka <46210417+libin-ka@users.noreply.github.com> --- fpga/xc2s50-5-tq144.ucf | 45 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 fpga/xc2s50-5-tq144.ucf diff --git a/fpga/xc2s50-5-tq144.ucf b/fpga/xc2s50-5-tq144.ucf new file mode 100644 index 000000000..0964e1acf --- /dev/null +++ b/fpga/xc2s50-5-tq144.ucf @@ -0,0 +1,45 @@ +# See the schematic for the pin assignment. + +NET "adc_d<0>" LOC = "P54" ; +NET "adc_d<1>" LOC = "P57" ; +NET "adc_d<2>" LOC = "P59" ; +NET "adc_d<3>" LOC = "P60" ; +NET "adc_d<4>" LOC = "P62" ; +NET "adc_d<5>" LOC = "P63" ; +NET "adc_d<6>" LOC = "P65" ; +NET "adc_d<7>" LOC = "P67" ; +#NET "cross_hi" LOC = "P88" ; +#NET "miso" LOC = "P40" ; +NET "adc_clk" LOC = "P75" ; +NET "adc_noe" LOC = "P74" ; +NET "ck_1356meg" LOC = "P15" ; +NET "ck_1356megb" LOC = "P12" ; +NET "cross_lo" LOC = "P19" ; +NET "dbg" LOC = "P112" ; +NET "mosi" LOC = "P80" ; +NET "ncs" LOC = "P79" ; +NET "pck0" LOC = "P91" ; +NET "pwr_hi" LOC = "P31" ; +NET "pwr_lo" LOC = "P30" ; +NET "pwr_oe1" LOC = "P28" ; +NET "pwr_oe2" LOC = "P27" ; +NET "pwr_oe3" LOC = "P26" ; +NET "pwr_oe4" LOC = "P21" ; +NET "spck" LOC = "P88" ; +NET "ssp_clk" LOC = "P43" ; +NET "ssp_din" LOC = "P99" ; +NET "ssp_dout" LOC = "P94" ; +NET "ssp_frame" LOC = "P100" ; + +# definition of Clock nets: +NET "ck_1356meg" TNM_NET = "clk_net_1356" ; +NET "ck_1356megb" TNM_NET = "clk_net_1356b"; +NET "pck0" TNM_NET = "clk_net_pck0" ; +NET "spck" TNM_NET = "clk_net_spck" ; + +# Timing specs of clock nets: +TIMEGRP "clk_net_1356_all" = "clk_net_1356" "clk_net_1356b" ; +TIMESPEC "TS_1356MHz" = PERIOD "clk_net_1356_all" 74 ns HIGH 37 ns ; +TIMESPEC "TS_24MHz" = PERIOD "clk_net_pck0" 42 ns HIGH 21 ns ; +TIMESPEC "TS_4MHz" = PERIOD "clk_net_spck" 250 ns HIGH 125 ns ; + From 87266654f7a1731c8ea8445549f0d6387c0af4d3 Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Wed, 4 Dec 2024 08:16:55 +0100 Subject: [PATCH 32/39] MIFARE Plus 4b UID: fix signature check --- CHANGELOG.md | 1 + client/src/cmdhfmfp.c | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f4553467a..86df97e12 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ All notable changes to this project will be documented in this file. This project uses the changelog in accordance with [keepchangelog](http://keepachangelog.com/). Please use this to write notable changes, which is not the same as git commit log... ## [unreleased][unreleased] +- Fixed `hf mfp info` fix signature check on 4b UID cards (@doegox) - Automatically set maximum read/write block when using predefined types in `hf_mf_ultimatecard` script (@piotrva) - Changed SPI flash detection to calculate the size instead of table lookup, updated spi_flash_decode.py script with more ICs (@ANTodorov) - Fixed `hf/lf tune` segfault when called from script (@doegox) diff --git a/client/src/cmdhfmfp.c b/client/src/cmdhfmfp.c index 9d9ecb880..ab5ce8f46 100644 --- a/client/src/cmdhfmfp.c +++ b/client/src/cmdhfmfp.c @@ -333,10 +333,12 @@ static int CmdHFMFPInfo(const char *Cmd) { // version check uint8_t version[30] = {0}; + uint8_t uid7b[7] = {0}; int version_len = sizeof(version); if (get_plus_version(version, &version_len) == PM3_SUCCESS) { plus_print_version(version); supportVersion = true; + memcpy(uid7b, version + 14, 7); } else { // info about 14a part, historical bytes. infoHF14A(false, false, false); @@ -346,7 +348,11 @@ static int CmdHFMFPInfo(const char *Cmd) { uint8_t signature[56] = {0}; int signature_len = sizeof(signature); if (get_plus_signature(signature, &signature_len) == PM3_SUCCESS) { - plus_print_signature(card.uid, card.uidlen, signature, signature_len); + if (supportVersion) { + plus_print_signature(uid7b, 7, signature, signature_len); + } else { + plus_print_signature(card.uid, card.uidlen, signature, signature_len); + } supportSignature = true; } From 34b2a3175f05a8e0bb8b3ff2f030472e9d3b53a4 Mon Sep 17 00:00:00 2001 From: nvx Date: Wed, 4 Dec 2024 22:20:18 +1000 Subject: [PATCH 33/39] fis MF3ICD40 (D40) secure channel crypto --- CHANGELOG.md | 1 + client/src/mifare/desfirecore.c | 7 +++++-- client/src/mifare/desfiresecurechan.c | 5 ++++- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 86df97e12..78c36987e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ All notable changes to this project will be documented in this file. This project uses the changelog in accordance with [keepchangelog](http://keepachangelog.com/). Please use this to write notable changes, which is not the same as git commit log... ## [unreleased][unreleased] +- Fixed DESFire D40 secure channel crypto (@nvx) - Fixed `hf mfp info` fix signature check on 4b UID cards (@doegox) - Automatically set maximum read/write block when using predefined types in `hf_mf_ultimatecard` script (@piotrva) - Changed SPI flash detection to calculate the size instead of table lookup, updated spi_flash_decode.py script with more ICs (@ANTodorov) diff --git a/client/src/mifare/desfirecore.c b/client/src/mifare/desfirecore.c index 7bf692184..ac44508f2 100644 --- a/client/src/mifare/desfirecore.c +++ b/client/src/mifare/desfirecore.c @@ -1222,14 +1222,17 @@ static int DesfireAuthenticateEV1(DesfireContext_t *dctx, DesfireSecureChannel s // - Encrypt our response if (secureChannel == DACd40) { + // Original DESFire (MF3ICD40) silicon can only do encryption operations, so all PCD + // side operations must be decrypt, even when encrypting when doing D40 compatible + // secure channel operations memset(IV, 0, DESFIRE_MAX_CRYPTO_BLOCK_SIZE); - DesfireCryptoEncDecEx(dctx, DCOMainKey, RndA, rndlen, encRndA, true, true, IV); + DesfireCryptoEncDecEx(dctx, DCOMainKey, RndA, rndlen, encRndA, true, false, IV); memcpy(both, encRndA, rndlen); bin_xor(rotRndB, encRndA, rndlen); memset(IV, 0, DESFIRE_MAX_CRYPTO_BLOCK_SIZE); - DesfireCryptoEncDecEx(dctx, DCOMainKey, rotRndB, rndlen, encRndB, true, true, IV); + DesfireCryptoEncDecEx(dctx, DCOMainKey, rotRndB, rndlen, encRndB, true, false, IV); memcpy(both + rndlen, encRndB, rndlen); } else if (secureChannel == DACEV1) { diff --git a/client/src/mifare/desfiresecurechan.c b/client/src/mifare/desfiresecurechan.c index d35833b4d..85e2d7d94 100644 --- a/client/src/mifare/desfiresecurechan.c +++ b/client/src/mifare/desfiresecurechan.c @@ -313,6 +313,10 @@ static void DesfireSecureChannelEncodeD40(DesfireContext_t *ctx, uint8_t cmd, ui size_t srcmaclen = padded_data_length(srcdatalen - hdrlen, desfire_get_key_block_length(ctx->keyType)); uint8_t mac[32] = {0}; + PrintAndLogEx(DEBUG, "MACing"); + // Even though original DESFire (MF3ICD40) silicon can only encrypt which means normally + // every PCD operation must be decrypt, verifying a MAC involves the same operation on both + // sides so this is still encrypt here DesfireCryptoEncDecEx(ctx, DCOSessionKeyMac, data, srcmaclen, NULL, true, true, mac); if (DesfireEV1D40TransmitMAC(ctx, cmd)) { @@ -889,4 +893,3 @@ bool PrintChannelModeWarning(uint8_t cmd, DesfireSecureChannel secureChannel, De return found; } - From 5bfd1239ae6d96ea36e99faeb4b4566f0a419a36 Mon Sep 17 00:00:00 2001 From: Lucifer Voeltner Date: Thu, 5 Dec 2024 10:41:26 +0700 Subject: [PATCH 34/39] Add new facility static hotel key --- client/dictionaries/mfc_default_keys.dic | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/client/dictionaries/mfc_default_keys.dic b/client/dictionaries/mfc_default_keys.dic index f3a339c93..8ad4d8f6e 100644 --- a/client/dictionaries/mfc_default_keys.dic +++ b/client/dictionaries/mfc_default_keys.dic @@ -2758,8 +2758,10 @@ D37C8F1793F7 # # BW Kantine 56cf3acd90ca -542089792be2 +542089792be2 5420aeada758 #CSC Laundry 212223242555 717273747555 +# Hotel cards, BETECH brand, Vietnam +AAC34D9A4E65 \ No newline at end of file From b1a5df53d3431064bff2430cebd1ee113443812d Mon Sep 17 00:00:00 2001 From: Chris Simon Date: Thu, 5 Dec 2024 10:45:49 +0100 Subject: [PATCH 35/39] Update mfc_default_keys.dic Added Dutch Statistics Agency Signed-off-by: Chris Simon --- client/dictionaries/mfc_default_keys.dic | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/client/dictionaries/mfc_default_keys.dic b/client/dictionaries/mfc_default_keys.dic index 8ad4d8f6e..04b092ea4 100644 --- a/client/dictionaries/mfc_default_keys.dic +++ b/client/dictionaries/mfc_default_keys.dic @@ -2764,4 +2764,6 @@ D37C8F1793F7 212223242555 717273747555 # Hotel cards, BETECH brand, Vietnam -AAC34D9A4E65 \ No newline at end of file +AAC34D9A4E65 +# Dutch Statistics Agency (CBS) +DC7B15AA0938 From 2850d2aa0fe9236ddd0db178eb2cef3d1fbc3743 Mon Sep 17 00:00:00 2001 From: Chris Simon Date: Thu, 5 Dec 2024 10:48:52 +0100 Subject: [PATCH 36/39] Update CHANGELOG.md Signed-off-by: Chris Simon --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 78c36987e..6e07c4c9c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac - Added JEDEC information for SPI flash W25Q64JV (@ANTodorov) - Added special iclass legacy config cards in `hf iclass configcard` (@antiklesys) - Added simulation function to `hf iclass legrec` (@antiklesys) +- Added Dutch Statistics Agency default key (@eagle00789) ## [Orca.4.19552][2024-11-22] - Fixed `hf_legic.lua` - removed bit32 commands from the script (@diorch1968) From 565f3f1feb3a82752cd6c7481959ff55de675729 Mon Sep 17 00:00:00 2001 From: Piotr Rzeszut Date: Sat, 7 Dec 2024 00:34:29 +0100 Subject: [PATCH 37/39] Extend storage allocated for Mifare keys to 4095 keys. Update documentation on the feature and memory map --- CHANGELOG.md | 1 + doc/ext_flash_notes.md | 4 ++-- include/pmflash.h | 6 +++--- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e07c4c9c..5462c11be 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ All notable changes to this project will be documented in this file. This project uses the changelog in accordance with [keepchangelog](http://keepachangelog.com/). Please use this to write notable changes, which is not the same as git commit log... ## [unreleased][unreleased] +- Extended area for Mifare keys in SPI flash to hold 4095 keys (@piotrva) - Fixed DESFire D40 secure channel crypto (@nvx) - Fixed `hf mfp info` fix signature check on 4b UID cards (@doegox) - Automatically set maximum read/write block when using predefined types in `hf_mf_ultimatecard` script (@piotrva) diff --git a/doc/ext_flash_notes.md b/doc/ext_flash_notes.md index 96b968bc7..6e5fd4e02 100644 --- a/doc/ext_flash_notes.md +++ b/doc/ext_flash_notes.md @@ -63,8 +63,8 @@ Page 3: Page3 is used as follows by the Proxmark3 RDV4 firmware: * **MF_KEYS** - * offset: page 3 sector 9 (0x9) @ 3*0x10000+9*0x1000=0x39000 - * length: 2 sectors + * offset: page 3 sector 5 (0x5) @ 3*0x10000+5*0x1000=0x35000 + * length: 6 sectors * **ICLASS_KEYS** * offset: page 3 sector 11 (0xB) @ 3*0x10000+11*0x1000=0x3B000 diff --git a/include/pmflash.h b/include/pmflash.h index bbed4b12e..8f9f9c741 100644 --- a/include/pmflash.h +++ b/include/pmflash.h @@ -28,7 +28,7 @@ // 0x3E000 - 1 4kb sector = settings // 0x3D000 - 1 4kb sector = default T55XX keys dictionary // 0x3B000 - 1 4kb sector = default ICLASS keys dictionary -// 0x38000 - 3 4kb sectors = default MFC keys dictionary +// 0x35000 - 6 4kb sectors = default MFC keys dictionary // #ifndef FLASH_MEM_BLOCK_SIZE # define FLASH_MEM_BLOCK_SIZE 256 @@ -95,9 +95,9 @@ # define DEFAULT_ICLASS_KEYS_OFFSET_P(p64k) (DEFAULT_T55XX_KEYS_OFFSET_P(p64k) - DEFAULT_ICLASS_KEYS_LEN) #endif -// Reserved space for MIFARE Keys = 12 kb +// Reserved space for MIFARE Keys = 24 kb #ifndef DEFAULT_MF_KEYS_OFFSET -# define DEFAULT_MF_KEYS_LEN (0x3000) +# define DEFAULT_MF_KEYS_LEN (0x6000) # define DEFAULT_MF_KEYS_OFFSET (DEFAULT_ICLASS_KEYS_OFFSET - DEFAULT_MF_KEYS_LEN) # define DEFAULT_MF_KEYS_MAX ((DEFAULT_MF_KEYS_LEN - 2) / 6) #endif From 1b781aae9ff02dd8ae6c36abb5f0eac612d33d2c Mon Sep 17 00:00:00 2001 From: Piotr Rzeszut Date: Sat, 7 Dec 2024 00:35:37 +0100 Subject: [PATCH 38/39] Update documentation on SPI flash memory to use modern client function calls with dashes. --- doc/ext_flash_notes.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/ext_flash_notes.md b/doc/ext_flash_notes.md index 6e5fd4e02..6a86c7778 100644 --- a/doc/ext_flash_notes.md +++ b/doc/ext_flash_notes.md @@ -36,22 +36,22 @@ Therefore a flash address can be interpreted as such: Page 0: * available for user data -* to dump it: `mem dump f page0_dump o 0 l 65536` +* to dump it: `mem dump -f page0_dump -o 0 -l 65536` * to erase it: `mem wipe p 0` Page 1: * available for user data -* to dump it: `mem dump f page1_dump o 65536 l 65536` +* to dump it: `mem dump -f page1_dump -o 65536 -l 65536` * to erase it: `mem wipe p 1` Page 2: * available for user data -* to dump it: `mem dump f page2_dump o 131072 l 65536` +* to dump it: `mem dump -f page2_dump -o 131072 -l 65536` * to erase it: `mem wipe p 2` Page 3: * used by Proxmark3 RDV4 specific functions: flash signature and keys dictionaries, see below for details -* to dump it: `mem dump f page3_dump o 196608 l 65536` +* to dump it: `mem dump -f page3_dump -o 196608 -l 65536` * to erase it: * **Beware** it will erase your flash signature so better to back it up first as you won't be able to regenerate it by yourself! * edit the source code to enable Page 3 as a valid input in the `mem wipe` command. From b1b10c2bea49121d5afe8fcdd311dcb75b78e030 Mon Sep 17 00:00:00 2001 From: Piotr Rzeszut Date: Sat, 7 Dec 2024 00:36:52 +0100 Subject: [PATCH 39/39] As extending SPI flash storage for Mifare need to run init_rdv4 script for a proper operation add a note under compilation instructions --- doc/md/Use_of_Proxmark/0_Compilation-Instructions.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/doc/md/Use_of_Proxmark/0_Compilation-Instructions.md b/doc/md/Use_of_Proxmark/0_Compilation-Instructions.md index 63076e720..1a291e9cd 100644 --- a/doc/md/Use_of_Proxmark/0_Compilation-Instructions.md +++ b/doc/md/Use_of_Proxmark/0_Compilation-Instructions.md @@ -117,6 +117,17 @@ or proxmark3 /dev/ttyACM0 --flash --unlock-bootloader --image /tmp/my-bootrom.elf --image /tmp/my-fullimage.elf ``` +## Updating SPI flash structure and contents (RDV4.x, some PM3 Easy variants) +^[Top](#top) + +For the devices equipped with external SPI flash memory chip in some cases it might be essential to update the memory structure as well as to upload new keys from the dictionaries. To do so execute following command inside the client: + +``` +[usb] pm3 --> script run init_rdv4 +``` + +For more details prease refer to [this doc](./2_Configuration-and-Verification.md). + ### The button trick ^[Top](#top)