From 4cd1c512b9a1fd37175b09e86842c581d9755baf Mon Sep 17 00:00:00 2001 From: Ave Date: Sun, 20 Sep 2020 01:00:57 +0300 Subject: [PATCH 1/2] hf_aveful: Add Ultralight EV1 support, clean code, dynamically determine block size --- armsrc/Standalone/hf_aveful.c | 118 ++++++++++++++++++++++++++++++++-- 1 file changed, 111 insertions(+), 7 deletions(-) diff --git a/armsrc/Standalone/hf_aveful.c b/armsrc/Standalone/hf_aveful.c index a619459f5..2b8d6ef81 100644 --- a/armsrc/Standalone/hf_aveful.c +++ b/armsrc/Standalone/hf_aveful.c @@ -11,8 +11,15 @@ // Several parts of this code is based on code by Craig Young from HF_YOUNG // This code does not: -// - Account for cards with authentication (MFU EV1 etc) -// - Determine if cards have block count that's not the same as the BLOCKS def +// - Account for cards with non-default keys on authentication (MFU EV1 etc) + +// This code is designed to work with: +// - MIFARE Ultralight +// - MIFARE Ultralight EV1 (default keys) +// - MIFARE Ultralight Nano (untested, but should work) +// - Infineon My-d Move (without password set) +// - Infineon My-d Move Lean +// - Any other Ultralight clones that have no auth and MAX_DEFAULT_BLOCKS (16) blocks #include "standalone.h" // standalone definitions #include "proxmark3_arm.h" @@ -22,10 +29,11 @@ #include "dbprint.h" #include "ticks.h" // SpinDelay +#include "protocols.h" // MIFARE_ULEV1_VERSION, MIFARE_ULEV1_READSIG +#include // memcmp #include "mifareutil.h" #include "iso14443a.h" -#define BLOCKS 16 #define SAK 0x00 #define ATQA0 0x44 #define ATQA1 0x00 @@ -34,6 +42,15 @@ #define STATE_READ 1 #define STATE_EMUL 2 +// Taken from cmdhfmfu.c, increased by 01h to be 1 indexed +#define MAX_UL_BLOCKS 0x10 +#define MAX_UL_NANO_40 0x0B +#define MAX_ULEV1a_BLOCKS 0x14 +#define MAX_ULEV1b_BLOCKS 0x29 +#define MAX_MY_D_MOVE 0x26 +#define MAX_MY_D_MOVE_LEAN 0x10 +#define MAX_DEFAULT_BLOCKS 0x10 + typedef struct { uint8_t uid[10]; uint8_t uidlen; @@ -41,6 +58,56 @@ typedef struct { uint8_t sak; } PACKED card_clone_t; +int get_block_count(iso14a_card_select_t card, uint8_t version[], uint16_t version_len); +uint16_t get_ev1_version(iso14a_card_select_t card, uint8_t *version); +uint16_t get_ev1_signature(iso14a_card_select_t card, uint8_t *signature); + +uint16_t get_ev1_version(iso14a_card_select_t card, uint8_t *version) { + return mifare_sendcmd(MIFARE_ULEV1_VERSION, NULL, 0, version, NULL, NULL); +} + +uint16_t get_ev1_signature(iso14a_card_select_t card, uint8_t *signature) { + uint8_t cmd[4] = {MIFARE_ULEV1_READSIG, 0x00, 0x00, 0x00}; + AddCrc14A(cmd, 2); + + ReaderTransmit(cmd, sizeof(cmd), NULL); + + return ReaderReceive(signature, NULL); +} + +int get_block_count(iso14a_card_select_t card, uint8_t version[], uint16_t version_len) { + // Default to MAX_DEFAULT_BLOCKS blocks + int block_count = MAX_DEFAULT_BLOCKS; + // Most of this code is from cmdhfmfu.c + // Infineon manufacturer ID + if (card.uid[0] == 0x05) { + // Infinition MY-D tests Exam high nibble + uint8_t nib = (card.uid[1] & 0xf0) >> 4; + switch (nib) { + case 3: + block_count = MAX_MY_D_MOVE; + break; // or SLE 66R01P // 38 pages of 4 bytes + case 7: + block_count = MAX_MY_D_MOVE_LEAN; + break; // or SLE 66R01L // 16 pages of 4 bytes + } + } else { + // Moved this from case to if as I only care about non-ultralight ev0. + if (version_len == 0x0A) { + if (memcmp(version, "\x00\x04\x03\x01\x01\x00\x0B", 7) == 0) { block_count = MAX_ULEV1a_BLOCKS; } + else if (memcmp(version, "\x00\x04\x03\x01\x02\x00\x0B", 7) == 0) { block_count = MAX_UL_NANO_40; } + else if (memcmp(version, "\x00\x04\x03\x02\x01\x00\x0B", 7) == 0) { block_count = MAX_ULEV1a_BLOCKS; } + else if (memcmp(version, "\x00\x04\x03\x01\x01\x00\x0E", 7) == 0) { block_count = MAX_ULEV1b_BLOCKS; } + else if (memcmp(version, "\x00\x04\x03\x02\x01\x00\x0E", 7) == 0) { block_count = MAX_ULEV1b_BLOCKS; } + else if (memcmp(version, "\x00\x34\x21\x01\x01\x00\x0E", 7) == 0) { block_count = MAX_ULEV1b_BLOCKS; } // Mikron JSC Russia EV1 41 pages tag + else if (memcmp(version, "\x00\x34\x21\x01\x01\x00\x0E", 7) == 0) { block_count = MAX_UL_BLOCKS; } + else if (version[2] == 0x03) { block_count = MAX_ULEV1a_BLOCKS; } + } + } + + return block_count; +} + void ModInfo(void) { DbpString(" HF Mifare Ultralight read/simulation by Ave Ozkal"); } @@ -88,10 +155,19 @@ void RunMod(void) { iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); iso14443a_select_card(NULL, NULL, NULL, true, 0, true); bool read_successful = true; + + // Get version and re-select card as UL EV0s like to shut off after a 0x60 + uint8_t version[10] = {0x00}; + uint16_t version_len = 0; + version_len = get_ev1_version(card, version); + iso14443a_select_card(NULL, NULL, NULL, true, 0, true); + + int block_count = get_block_count(card, version, version_len); + Dbprintf("Card was determined as having %d blocks.", block_count); Dbprintf("Contents:"); - for (int i = 0; i < BLOCKS; i++) { - uint8_t dataout[4] = {0x00}; + for (int i = 0; i < block_count; i++) { + uint8_t dataout[16] = {0x00}; if (mifare_ultra_readblock(i, dataout)) { // If there's an error reading, go back to search state read_successful = false; @@ -103,18 +179,46 @@ void RunMod(void) { Dbhexdump(4, dataout, 0); } + // It's not the best way to determine this, + // but with what I'm trying to support It Should Be Okay + bool is_ev1 = (version_len != 0) && (block_count != 16); + if (read_successful) { + uint8_t signature[32] = {0x00}; + if (is_ev1) { + get_ev1_signature(card, signature); + } + // Fill first 14 blocks with 0x00 (see comment above) + for (int i = 0; i < 14; i++) { + uint8_t dataout[4] = {0x00, 0x00, 0x00, 0x00}; + + if (is_ev1 && (i == 0 || i == 1)) { + // On block 0 and 1, set version on EV1 + memcpy(dataout, version + (i * 4), 4); + } else if (i == 2) { + // On block 2, set last byte to the card's block count + dataout[3] = block_count; + } else if (is_ev1 && ((i > 2 && i < 11))) { + // On 3-10 add signature on EV1 + memcpy(dataout, signature + (i * 4), 4); + } else if (is_ev1 && (i > 10)) { + // On 11-14 set tearing to 0xBD on EV1 + dataout[3] = 0xBD; + } + + emlSetMem_xt(dataout, i, 1, 4); + } Dbprintf("Successfully loaded into emulator memory..."); state = STATE_EMUL; } else { Dbprintf("Read failure, going back to search state."); state = STATE_SEARCH; } - } else if (state == 2) { + } else if (state == STATE_EMUL) { uint8_t flags = FLAG_7B_UID_IN_DATA; Dbprintf("Starting simulation, press pm3-button to stop and go back to search state."); - SimulateIso14443aTag(2, flags, card.uid); + SimulateIso14443aTag(7, flags, card.uid); // Go back to search state if user presses pm3-button state = STATE_SEARCH; From fe22eafcf13709e664e08937aac446ecdef8c666 Mon Sep 17 00:00:00 2001 From: Ave Date: Sun, 20 Sep 2020 02:07:21 +0300 Subject: [PATCH 2/2] hf_aveful: Pull proper counter and tearing, bugfixes --- armsrc/Standalone/hf_aveful.c | 37 +++++++++++++++++++++++++++-------- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/armsrc/Standalone/hf_aveful.c b/armsrc/Standalone/hf_aveful.c index 2b8d6ef81..b59f1a34a 100644 --- a/armsrc/Standalone/hf_aveful.c +++ b/armsrc/Standalone/hf_aveful.c @@ -29,7 +29,7 @@ #include "dbprint.h" #include "ticks.h" // SpinDelay -#include "protocols.h" // MIFARE_ULEV1_VERSION, MIFARE_ULEV1_READSIG +#include "protocols.h" // MIFARE_ULEV1_VERSION, MIFARE_ULEV1_READSIG, MIFARE_ULEV1_READ_CNT, MIFARE_ULEV1_CHECKTEAR #include // memcmp #include "mifareutil.h" #include "iso14443a.h" @@ -61,18 +61,32 @@ typedef struct { int get_block_count(iso14a_card_select_t card, uint8_t version[], uint16_t version_len); uint16_t get_ev1_version(iso14a_card_select_t card, uint8_t *version); uint16_t get_ev1_signature(iso14a_card_select_t card, uint8_t *signature); +uint16_t get_ev1_counter(iso14a_card_select_t card, uint8_t counter, uint8_t *response); +uint16_t get_ev1_tearing(iso14a_card_select_t card, uint8_t counter, uint8_t *response); uint16_t get_ev1_version(iso14a_card_select_t card, uint8_t *version) { return mifare_sendcmd(MIFARE_ULEV1_VERSION, NULL, 0, version, NULL, NULL); } -uint16_t get_ev1_signature(iso14a_card_select_t card, uint8_t *signature) { +uint16_t get_ev1_signature(iso14a_card_select_t card, uint8_t *response) { uint8_t cmd[4] = {MIFARE_ULEV1_READSIG, 0x00, 0x00, 0x00}; AddCrc14A(cmd, 2); - ReaderTransmit(cmd, sizeof(cmd), NULL); + return ReaderReceive(response, NULL); +} - return ReaderReceive(signature, NULL); +uint16_t get_ev1_counter(iso14a_card_select_t card, uint8_t counter, uint8_t *response) { + uint8_t cmd[4] = {MIFARE_ULEV1_READ_CNT, counter, 0x00, 0x00}; + AddCrc14A(cmd, 2); + ReaderTransmit(cmd, sizeof(cmd), NULL); + return ReaderReceive(response, NULL); +} + +uint16_t get_ev1_tearing(iso14a_card_select_t card, uint8_t counter, uint8_t *response) { + uint8_t cmd[4] = {MIFARE_ULEV1_CHECKTEAR, counter, 0x00, 0x00}; + AddCrc14A(cmd, 2); + ReaderTransmit(cmd, sizeof(cmd), NULL); + return ReaderReceive(response, NULL); } int get_block_count(iso14a_card_select_t card, uint8_t version[], uint16_t version_len) { @@ -184,10 +198,11 @@ void RunMod(void) { bool is_ev1 = (version_len != 0) && (block_count != 16); if (read_successful) { - uint8_t signature[32] = {0x00}; + uint8_t signature[34] = {0x00}; if (is_ev1) { get_ev1_signature(card, signature); } + Dbprintf("Preparing emulator memory with:"); // Fill first 14 blocks with 0x00 (see comment above) for (int i = 0; i < 14; i++) { uint8_t dataout[4] = {0x00, 0x00, 0x00, 0x00}; @@ -200,12 +215,18 @@ void RunMod(void) { dataout[3] = block_count; } else if (is_ev1 && ((i > 2 && i < 11))) { // On 3-10 add signature on EV1 - memcpy(dataout, signature + (i * 4), 4); + memcpy(dataout, signature + ((i - 3) * 4), 4); } else if (is_ev1 && (i > 10)) { - // On 11-14 set tearing to 0xBD on EV1 - dataout[3] = 0xBD; + // On 11-14 read and set counter and tearing on EV1 + uint8_t counter[5]; + uint8_t tearing[3]; + get_ev1_counter(card, i - 11, counter); + get_ev1_tearing(card, i - 11, tearing); + memcpy(dataout, counter, 3); + memcpy(dataout + 3, tearing, 1); } + Dbhexdump(4, dataout, 0); emlSetMem_xt(dataout, i, 1, 4); } Dbprintf("Successfully loaded into emulator memory...");