//----------------------------------------------------------------------------- // Ultralight Code (c) 2013,2014 Midnitesnake & Andy Davies of Pentura // 2015,2016,2017 Iceman, Marshmellow // This code is licensed to you under the terms of the GNU GPL, version 2 or, // at your option, any later version. See the LICENSE.txt file for the text of // the license. //----------------------------------------------------------------------------- // High frequency MIFARE ULTRALIGHT (C) commands //----------------------------------------------------------------------------- #include "cmdhfmfu.h" #include #include "cmdparser.h" #include "commonutil.h" #include "crypto/libpcrypto.h" #include "des.h" #include "aes.h" #include "cmdhfmf.h" #include "cmdhf14a.h" #include "comms.h" #include "fileutils.h" #include "protocols.h" #include "generator.h" #include "nfc/ndef.h" #include "cliparser.h" #include "cmdmain.h" #include "amiibo.h" // amiiboo fcts #include "base64.h" #define MAX_UL_BLOCKS 0x0F #define MAX_ULC_BLOCKS 0x2F #define MAX_ULEV1a_BLOCKS 0x13 #define MAX_ULEV1b_BLOCKS 0x28 #define MAX_NTAG_203 0x29 #define MAX_NTAG_210 0x13 #define MAX_NTAG_212 0x28 #define MAX_NTAG_213 0x2C #define MAX_NTAG_215 0x86 #define MAX_NTAG_216 0xE6 #define MAX_NTAG_I2C_1K 0xE9 #define MAX_MY_D_NFC 0xFF #define MAX_MY_D_MOVE 0x25 #define MAX_MY_D_MOVE_LEAN 0x0F #define MAX_UL_NANO_40 0x0A static int CmdHelp(const char *Cmd); uint8_t default_3des_keys[][16] = { { 0x42, 0x52, 0x45, 0x41, 0x4b, 0x4d, 0x45, 0x49, 0x46, 0x59, 0x4f, 0x55, 0x43, 0x41, 0x4e, 0x21 }, // 3des std key { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // all zeroes { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f }, // 0x00-0x0F { 0x49, 0x45, 0x4D, 0x4B, 0x41, 0x45, 0x52, 0x42, 0x21, 0x4E, 0x41, 0x43, 0x55, 0x4F, 0x59, 0x46 }, // NFC-key { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 }, // all ones { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }, // all FF { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF } // 11 22 33 }; uint8_t default_pwd_pack[][4] = { {0xFF, 0xFF, 0xFF, 0xFF}, // PACK 0x00,0x00 -- factory default {0x4E, 0x45, 0x78, 0x54}, }; uint32_t UL_TYPES_ARRAY[] = { UNKNOWN, UL, UL_C, UL_EV1_48, UL_EV1_128, NTAG, NTAG_203, NTAG_210, NTAG_212, NTAG_213, NTAG_215, NTAG_216, MY_D, MY_D_NFC, MY_D_MOVE, MY_D_MOVE_NFC, MY_D_MOVE_LEAN, FUDAN_UL, UL_EV1, NTAG_213_F, NTAG_216_F, UL_NANO_40, NTAG_I2C_1K, NTAG_213_TT, NTAG_213_C, NTAG_210u }; uint8_t UL_MEMORY_ARRAY[ARRAYLEN(UL_TYPES_ARRAY)] = { MAX_UL_BLOCKS, MAX_UL_BLOCKS, MAX_ULC_BLOCKS, MAX_ULEV1a_BLOCKS, MAX_ULEV1b_BLOCKS, MAX_NTAG_203, MAX_NTAG_203, MAX_NTAG_210, MAX_NTAG_212, MAX_NTAG_213, MAX_NTAG_215, MAX_NTAG_216, MAX_UL_BLOCKS, MAX_MY_D_NFC, MAX_MY_D_MOVE, MAX_MY_D_MOVE, MAX_MY_D_MOVE_LEAN, MAX_UL_BLOCKS, MAX_ULEV1a_BLOCKS, MAX_NTAG_213, MAX_NTAG_216, MAX_UL_NANO_40, MAX_NTAG_I2C_1K, MAX_NTAG_213, MAX_NTAG_213, MAX_NTAG_210 }; //------------------------------------ // get version nxp product type static char *getProductTypeStr(uint8_t id) { static char buf[20]; char *retStr = buf; switch (id) { case 3: sprintf(retStr, "%02X, Ultralight", id); break; case 4: sprintf(retStr, "%02X, NTAG", id); break; default: sprintf(retStr, "%02X, unknown", id); break; } return buf; } /* The 7 MSBits (=n) code the storage size itself based on 2^n, the LSBit is set to '0' if the size is exactly 2^n and set to '1' if the storage size is between 2^n and 2^(n+1). */ static char *getUlev1CardSizeStr(uint8_t fsize) { static char buf[40]; char *retStr = buf; memset(buf, 0, sizeof(buf)); uint16_t usize = 1 << ((fsize >> 1) + 1); uint16_t lsize = 1 << (fsize >> 1); // is LSB set? if (fsize & 1) sprintf(retStr, "%02X, (%u <-> %u bytes)", fsize, usize, lsize); else sprintf(retStr, "%02X, (%u bytes)", fsize, lsize); return buf; } int ul_read_uid(uint8_t *uid) { if (uid == NULL) { PrintAndLogEx(WARNING, "NUll parameter UID"); return PM3_ESOFT; } // read uid from tag clearCommandBuffer(); SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_NO_RATS, 0, 0, NULL, 0); PacketResponseNG resp; WaitForResponse(CMD_ACK, &resp); iso14a_card_select_t card; memcpy(&card, (iso14a_card_select_t *)resp.data.asBytes, sizeof(iso14a_card_select_t)); uint64_t select_status = resp.oldarg[0]; // 0: couldn't read // 1: OK with ATS // 2: OK, no ATS // 3: proprietary Anticollision if (select_status == 0) { PrintAndLogEx(WARNING, "iso14443a card select failed"); return PM3_ESOFT; } if (card.uidlen != 7) { PrintAndLogEx(WARNING, "Wrong sized UID, expected 7bytes got %d", card.uidlen); return PM3_ESOFT; } memcpy(uid, card.uid, 7); return PM3_SUCCESS; } static void ul_switch_on_field(void) { clearCommandBuffer(); SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_NO_DISCONNECT | ISO14A_NO_RATS, 0, 0, NULL, 0); } static int ul_send_cmd_raw(uint8_t *cmd, uint8_t cmdlen, uint8_t *response, uint16_t responseLength) { clearCommandBuffer(); SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_RAW | ISO14A_NO_DISCONNECT | ISO14A_APPEND_CRC | ISO14A_NO_RATS, cmdlen, 0, cmd, cmdlen); PacketResponseNG resp; if (!WaitForResponseTimeout(CMD_ACK, &resp, 1500)) return -1; if (!resp.oldarg[0] && responseLength) return -1; uint16_t resplen = (resp.oldarg[0] < responseLength) ? resp.oldarg[0] : responseLength; memcpy(response, resp.data.asBytes, resplen); return resplen; } static bool ul_select(iso14a_card_select_t *card) { ul_switch_on_field(); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) { PrintAndLogEx(WARNING, "timeout while waiting for reply."); DropField(); return false; } else { uint16_t len = (resp.oldarg[1] & 0xFFFF); if (len == 0) { PrintAndLogEx(WARNING, "iso14443a card select failed"); DropField(); return false; } if (card) memcpy(card, resp.data.asBytes, sizeof(iso14a_card_select_t)); } return true; } // This read command will at least return 16bytes. static int ul_read(uint8_t page, uint8_t *response, uint16_t responseLength) { uint8_t cmd[] = {ISO14443A_CMD_READBLOCK, page}; int len = ul_send_cmd_raw(cmd, sizeof(cmd), response, responseLength); return len; } static int ul_comp_write(uint8_t page, uint8_t *data, uint8_t datalen) { if (data == NULL) return -1; uint8_t cmd[18]; memset(cmd, 0x00, sizeof(cmd)); datalen = (datalen > 16) ? 16 : datalen; cmd[0] = ISO14443A_CMD_WRITEBLOCK; cmd[1] = page; memcpy(cmd + 2, data, datalen); uint8_t response[1] = {0xFF}; ul_send_cmd_raw(cmd, 2 + datalen, response, sizeof(response)); // ACK if (response[0] == 0x0a) return 0; // NACK return -1; } static int ulc_requestAuthentication(uint8_t *nonce, uint16_t nonceLength) { uint8_t cmd[] = {MIFARE_ULC_AUTH_1, 0x00}; int len = ul_send_cmd_raw(cmd, sizeof(cmd), nonce, nonceLength); return len; } static int ulc_authentication(uint8_t *key, bool switch_off_field) { clearCommandBuffer(); SendCommandMIX(CMD_HF_MIFAREUC_AUTH, switch_off_field, 0, 0, key, 16); PacketResponseNG resp; if (!WaitForResponseTimeout(CMD_ACK, &resp, 1500)) return 0; if (resp.oldarg[0] == 1) return 1; return 0; } static int try_default_3des_keys(uint8_t **correct_key) { PrintAndLogEx(INFO, "Trying some default 3des keys"); for (uint8_t i = 0; i < ARRAYLEN(default_3des_keys); ++i) { uint8_t *key = default_3des_keys[i]; if (ulc_authentication(key, true)) { *correct_key = key; return PM3_SUCCESS; } } return PM3_ESOFT; } static int ulev1_requestAuthentication(uint8_t *pwd, uint8_t *pack, uint16_t packLength) { uint8_t cmd[] = {MIFARE_ULEV1_AUTH, pwd[0], pwd[1], pwd[2], pwd[3]}; int len = ul_send_cmd_raw(cmd, sizeof(cmd), pack, packLength); // NACK tables different tags, but between 0-9 is a NEGATIVE response. // ACK == 0xA if (len == 1 && pack[0] <= 0x09) return -1; return len; } static int ul_auth_select(iso14a_card_select_t *card, TagTypeUL_t tagtype, bool hasAuthKey, uint8_t *authkey, uint8_t *pack, uint8_t packSize) { if (hasAuthKey && (tagtype & UL_C)) { //will select card automatically and close connection on error if (!ulc_authentication(authkey, false)) { PrintAndLogEx(WARNING, "Authentication Failed UL-C"); return PM3_ESOFT; } } else { if (!ul_select(card)) return PM3_ESOFT; if (hasAuthKey) { if (ulev1_requestAuthentication(authkey, pack, packSize) == -1) { DropField(); PrintAndLogEx(WARNING, "Authentication Failed UL-EV1/NTAG"); return PM3_ESOFT; } } } return PM3_SUCCESS; } static int ulev1_getVersion(uint8_t *response, uint16_t responseLength) { uint8_t cmd[] = {MIFARE_ULEV1_VERSION}; int len = ul_send_cmd_raw(cmd, sizeof(cmd), response, responseLength); return len; } static int ulev1_readCounter(uint8_t counter, uint8_t *response, uint16_t responseLength) { uint8_t cmd[] = {MIFARE_ULEV1_READ_CNT, counter}; int len = ul_send_cmd_raw(cmd, sizeof(cmd), response, responseLength); return len; } static int ulev1_readTearing(uint8_t counter, uint8_t *response, uint16_t responseLength) { uint8_t cmd[] = {MIFARE_ULEV1_CHECKTEAR, counter}; int len = ul_send_cmd_raw(cmd, sizeof(cmd), response, responseLength); return len; } static int ulev1_readSignature(uint8_t *response, uint16_t responseLength) { uint8_t cmd[] = {MIFARE_ULEV1_READSIG, 0x00}; int len = ul_send_cmd_raw(cmd, sizeof(cmd), response, responseLength); return len; } // Fudan check checks for which error is given for a command with incorrect crc // NXP UL chip responds with 01, fudan 00. // other possible checks: // send a0 + crc // UL responds with 00, fudan doesn't respond // or // send a200 + crc // UL doesn't respond, fudan responds with 00 // or // send 300000 + crc (read with extra byte(s)) // UL responds with read of page 0, fudan doesn't respond. // // make sure field is off before calling this function static int ul_fudan_check(void) { iso14a_card_select_t card; if (!ul_select(&card)) return UL_ERROR; uint8_t cmd[4] = {0x30, 0x00, 0x02, 0xa7}; //wrong crc on purpose should be 0xa8 clearCommandBuffer(); SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_RAW | ISO14A_NO_DISCONNECT | ISO14A_NO_RATS, 4, 0, cmd, sizeof(cmd)); PacketResponseNG resp; if (!WaitForResponseTimeout(CMD_ACK, &resp, 1500)) return UL_ERROR; if (resp.oldarg[0] != 1) return UL_ERROR; return (!resp.data.asBytes[0]) ? FUDAN_UL : UL; //if response == 0x00 then Fudan, else Genuine NXP } static int ul_print_default(uint8_t *data, uint8_t *real_uid) { uint8_t uid[7]; uid[0] = data[0]; uid[1] = data[1]; uid[2] = data[2]; uid[3] = data[4]; uid[4] = data[5]; uid[5] = data[6]; uid[6] = data[7]; bool mful_uid_layout = true; if (memcmp(uid, real_uid, 7) != 0) { mful_uid_layout = false; } PrintAndLogEx(SUCCESS, " UID: " _GREEN_("%s"), sprint_hex(real_uid, 7)); PrintAndLogEx(SUCCESS, " UID[0]: %02X, %s", real_uid[0], getTagInfo(real_uid[0])); if (real_uid[0] == 0x05 && ((real_uid[1] & 0xf0) >> 4) == 2) { // is infineon and 66RxxP uint8_t chip = (data[8] & 0xC7); // 11000111 mask, bit 3,4,5 RFU switch (chip) { case 0xC2: PrintAndLogEx(SUCCESS, " IC type: SLE 66R04P 770 Bytes"); break; //77 pages case 0xC4: PrintAndLogEx(SUCCESS, " IC type: SLE 66R16P 2560 Bytes"); break; //256 pages case 0xC6: PrintAndLogEx(SUCCESS, " IC type: SLE 66R32P 5120 Bytes"); break; //512 pages /2 sectors } } if (mful_uid_layout) { // CT (cascade tag byte) 0x88 xor SN0 xor SN1 xor SN2 int crc0 = 0x88 ^ uid[0] ^ uid[1] ^ uid[2]; if (data[3] == crc0) PrintAndLogEx(SUCCESS, " BCC0: %02X (" _GREEN_("ok") ")", data[3]); else PrintAndLogEx(NORMAL, " BCC0: %02X, crc should be %02X", data[3], crc0); int crc1 = uid[3] ^ uid[4] ^ uid[5] ^ uid[6]; if (data[8] == crc1) PrintAndLogEx(SUCCESS, " BCC1: %02X (" _GREEN_("ok") ")", data[8]); else PrintAndLogEx(NORMAL, " BCC1: %02X, crc should be %02X", data[8], crc1); PrintAndLogEx(SUCCESS, " Internal: %02X (%s)", data[9], (data[9] == 0x48) ? _GREEN_("default") : _RED_("not default")); } else { PrintAndLogEx(SUCCESS, "Blocks 0-2: %s", sprint_hex(data + 0, 12)); } PrintAndLogEx(SUCCESS, " Lock: %s - %s", sprint_hex(data + 10, 2), sprint_bin(data + 10, 2) ); PrintAndLogEx(SUCCESS, "OneTimePad: %s - %s", sprint_hex(data + 12, 4), sprint_bin(data + 12, 4) ); return PM3_SUCCESS; } static int ndef_get_maxsize(uint8_t *data) { // no NDEF message if (data[0] != 0xE1) return 0; if (data[2] == 0x06) return 48; else if (data[2] == 0x12) return 144; else if (data[2] == 0x3E) return 496; else if (data[2] == 0x6D) return 872; return 0; } static int ndef_print_CC(uint8_t *data) { // no NDEF message if (data[0] != 0xE1) return PM3_ESOFT; //NFC Forum Type 1,2,3,4 // // 4 has 1.1 (11) // b7, b6 major version // b5, b4 minor version // b3, b2 read // 00 always, 01 rfu, 10 proprietary, 11 rfu // b1, b0 write // 00 always, 01 rfo, 10 proprietary, 11 never uint8_t cc_write = data[1] & 0x03; uint8_t cc_read = (data[1] & 0x0C) >> 2; uint8_t cc_minor = (data[1] & 0x30) >> 4; uint8_t cc_major = (data[1] & 0xC0) >> 6; char wStr[50]; memset(wStr, 0, sizeof(wStr)); switch (cc_write) { case 0: sprintf(wStr, "Write access granted without any security"); break; case 1: sprintf(wStr, "RFU"); break; case 2: sprintf(wStr, "Proprietary"); break; case 3: sprintf(wStr, "No write access"); break; } char rStr[46]; memset(rStr, 0, sizeof(rStr)); switch (cc_read) { case 0: sprintf(rStr, "Read access granted without any security"); break; case 1: case 3: sprintf(rStr, "RFU"); break; case 2: sprintf(rStr, "Proprietary"); break; } PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "--- " _CYAN_("NDEF Message")); PrintAndLogEx(SUCCESS, "Capability Container: %s", sprint_hex(data, 4)); PrintAndLogEx(SUCCESS, " %02X: NDEF Magic Number", data[0]); // PrintAndLogEx(SUCCESS, " %02X : version %d.%d supported by tag", data[1], (data[1] & 0xF0) >> 4, data[1] & 0x0F); PrintAndLogEx(SUCCESS, " %02X: version %d.%d supported by tag", data[1], cc_major, cc_minor); PrintAndLogEx(SUCCESS, " : %s / %s", rStr, wStr); PrintAndLogEx(SUCCESS, " %02X: Physical Memory Size: %d bytes", data[2], data[2] * 8); if (data[2] == 0x06) PrintAndLogEx(SUCCESS, " %02X: NDEF Memory Size: %d bytes", data[2], 48); else if (data[2] == 0x12) PrintAndLogEx(SUCCESS, " %02X: NDEF Memory Size: %d bytes", data[2], 144); else if (data[2] == 0x3E) PrintAndLogEx(SUCCESS, " %02X: NDEF Memory Size: %d bytes", data[2], 496); else if (data[2] == 0x6D) PrintAndLogEx(SUCCESS, " %02X: NDEF Memory Size: %d bytes", data[2], 872); uint8_t msb3 = (data[3] & 0xE0) >> 5; uint8_t sf = (data[3] & 0x10) >> 4; uint8_t lb = (data[3] & 0x08) >> 3; uint8_t mlrule = (data[3] & 0x06) >> 1; uint8_t mbread = (data[3] & 0x01); PrintAndLogEx(SUCCESS, " Additional feature information"); PrintAndLogEx(SUCCESS, " %02X", data[3]); PrintAndLogEx(SUCCESS, " 00000000"); PrintAndLogEx(SUCCESS, " xxx - %02X: RFU (%s)", msb3, (msb3 == 0) ? _GREEN_("ok") : _RED_("fail")); PrintAndLogEx(SUCCESS, " x - %02X: %s special frame", sf, (sf) ? "support" : "don\'t support"); PrintAndLogEx(SUCCESS, " x - %02X: %s lock block", lb, (lb) ? "support" : "don\'t support"); PrintAndLogEx(SUCCESS, " xx - %02X: RFU (%s)", mlrule, (mlrule == 0) ? _GREEN_("ok") : _RED_("fail")); PrintAndLogEx(SUCCESS, " x - %02X: IC %s multiple block reads", mbread, (mbread) ? "support" : "don\'t support"); return PM3_SUCCESS; } int ul_print_type(uint32_t tagtype, uint8_t spaces) { if (spaces > 10) spaces = 10; char typestr[100]; memset(typestr, 0x00, sizeof(typestr)); if (tagtype & UL) snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("MIFARE Ultralight (MF0ICU1)"), spaces, ""); else if (tagtype & UL_C) snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("MIFARE Ultralight C (MF0ULC)"), spaces, ""); else if (tagtype & UL_NANO_40) snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("MIFARE Ultralight Nano 40bytes (MF0UNH00)"), spaces, ""); else if (tagtype & UL_EV1_48) snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("MIFARE Ultralight EV1 48bytes (MF0UL1101)"), spaces, ""); else if (tagtype & UL_EV1_128) snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("MIFARE Ultralight EV1 128bytes (MF0UL2101)"), spaces, ""); else if (tagtype & UL_EV1) snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("MIFARE Ultralight EV1 UNKNOWN"), spaces, ""); else if (tagtype & NTAG) snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("NTAG UNKNOWN"), spaces, ""); else if (tagtype & NTAG_203) snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("NTAG 203 144bytes (NT2H0301F0DT)"), spaces, ""); else if (tagtype & NTAG_210u) snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("NTAG 210u (micro) 48bytes (NT2L1001G0DU)"), spaces, ""); else if (tagtype & NTAG_210) snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("NTAG 210 48bytes (NT2L1011G0DU)"), spaces, ""); else if (tagtype & NTAG_212) snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("NTAG 212 128bytes (NT2L1211G0DU)"), spaces, ""); else if (tagtype & NTAG_213) snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("NTAG 213 144bytes (NT2H1311G0DU)"), spaces, ""); else if (tagtype & NTAG_213_F) snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("NTAG 213F 144bytes (NT2H1311F0DTL)"), spaces, ""); else if (tagtype & NTAG_213_C) snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("NTAG 213C 144bytes (NT2H1311C1DTL)"), spaces, ""); else if (tagtype & NTAG_213_TT) snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("NTAG 213TT 144bytes (NT2H1311TTDU)"), spaces, ""); else if (tagtype & NTAG_215) snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("NTAG 215 504bytes (NT2H1511G0DU)"), spaces, ""); else if (tagtype & NTAG_216) snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("NTAG 216 888bytes (NT2H1611G0DU)"), spaces, ""); else if (tagtype & NTAG_216_F) snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("NTAG 216F 888bytes (NT2H1611F0DTL)"), spaces, ""); else if (tagtype & NTAG_I2C_1K) snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("NTAG I2C 888bytes (NT3H1101FHK)"), spaces, ""); else if (tagtype & NTAG_I2C_2K) snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("NTAG I2C 1904bytes (NT3H1201FHK)"), spaces, ""); else if (tagtype & NTAG_I2C_1K_PLUS) snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("NTAG I2C plus 888bytes (NT3H2111FHK)"), spaces, ""); else if (tagtype & NTAG_I2C_2K_PLUS) snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("NTAG I2C plus 1912bytes (NT3H2211FHK)"), spaces, ""); else if (tagtype & MY_D) snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("INFINEON my-d\x99 (SLE 66RxxS)"), spaces, ""); else if (tagtype & MY_D_NFC) snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("INFINEON my-d\x99 NFC (SLE 66RxxP)"), spaces, ""); else if (tagtype & MY_D_MOVE) snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("INFINEON my-d\x99 move (SLE 66R01P)"), spaces, ""); else if (tagtype & MY_D_MOVE_NFC) snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("INFINEON my-d\x99 move NFC (SLE 66R01P)"), spaces, ""); else if (tagtype & MY_D_MOVE_LEAN) snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("INFINEON my-d\x99 move lean (SLE 66R01L)"), spaces, ""); else if (tagtype & FUDAN_UL) snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("FUDAN Ultralight Compatible (or other compatible)"), spaces, ""); else snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("Unknown %06x"), spaces, "", tagtype); bool ismagic = ((tagtype & MAGIC) == MAGIC); if (ismagic) snprintf(typestr + strlen(typestr), 4, " ("); snprintf(typestr + strlen(typestr), sizeof(typestr) - strlen(typestr), " %s ", (tagtype & MAGIC) ? _GREEN_("magic") : ""); tagtype &= ~(MAGIC); snprintf(typestr + strlen(typestr), sizeof(typestr) - strlen(typestr), "%s", (tagtype & MAGIC_1A) ? _GREEN_("Gen 1a") : ""); snprintf(typestr + strlen(typestr), sizeof(typestr) - strlen(typestr), "%s", (tagtype & MAGIC_1B) ? _GREEN_("Gen 1b") : ""); if (ismagic) snprintf(typestr + strlen(typestr), 4, " )"); PrintAndLogEx(SUCCESS, "%s", typestr); return PM3_SUCCESS; } static int ulc_print_3deskey(uint8_t *data) { PrintAndLogEx(INFO, " deskey1 [44/0x2C]: %s [%s]", sprint_hex(data, 4), sprint_ascii(data, 4)); PrintAndLogEx(INFO, " deskey1 [45/0x2D]: %s [%s]", sprint_hex(data + 4, 4), sprint_ascii(data + 4, 4)); PrintAndLogEx(INFO, " deskey2 [46/0x2E]: %s [%s]", sprint_hex(data + 8, 4), sprint_ascii(data + 8, 4)); PrintAndLogEx(INFO, " deskey2 [47/0x2F]: %s [%s]", sprint_hex(data + 12, 4), sprint_ascii(data + 12, 4)); PrintAndLogEx(INFO, "3des key: " _GREEN_("%s"), sprint_hex_inrow(SwapEndian64(data, 16, 8), 16)); return PM3_SUCCESS; } static int ulc_print_configuration(uint8_t *data) { PrintAndLogEx(NORMAL, "\n--- " _CYAN_("UL-C Configuration")); PrintAndLogEx(NORMAL, " Higher Lockbits [40/0x28]: %s - %s", sprint_hex(data, 4), sprint_bin(data, 2)); PrintAndLogEx(NORMAL, " Counter [41/0x29]: %s - %s", sprint_hex(data + 4, 4), sprint_bin(data + 4, 2)); bool validAuth = (data[8] >= 0x03 && data[8] <= 0x30); if (validAuth) PrintAndLogEx(NORMAL, " Auth0 [42/0x2A]: %s page %d/0x%02X and above need authentication", sprint_hex(data + 8, 4), data[8], data[8]); else { if (data[8] == 0) { PrintAndLogEx(NORMAL, " Auth0 [42/0x2A]: %s default", sprint_hex(data + 8, 4)); } else { PrintAndLogEx(NORMAL, " Auth0 [42/0x2A]: %s auth byte is out-of-range", sprint_hex(data + 8, 4)); } } PrintAndLogEx(NORMAL, " Auth1 [43/0x2B]: %s %s", sprint_hex(data + 12, 4), (data[12] & 1) ? "write access restricted" : "read and write access restricted" ); return PM3_SUCCESS; } static int ulev1_print_configuration(uint32_t tagtype, uint8_t *data, uint8_t startPage) { PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "--- " _CYAN_("Tag Configuration")); bool strg_mod_en = (data[0] & 2); uint8_t authlim = (data[4] & 0x07); bool nfc_cnf_prot_pwd = ((data[4] & 0x08) == 0x08); bool nfc_cnf_en = ((data[4] & 0x10) == 0x10); bool cfglck = ((data[4] & 0x40) == 0x40); bool prot = ((data[4] & 0x80) == 0x80); uint8_t vctid = data[5]; PrintAndLogEx(INFO, " cfg0 [%u/0x%02X]: %s", startPage, startPage, sprint_hex(data, 4)); if ((tagtype & (NTAG_213_F | NTAG_213_TT | NTAG_216_F))) { uint8_t mirror_conf = (data[0] & 0xC0); uint8_t mirror_byte = (data[0] & 0x30); bool sleep_en = (data[0] & 0x08); strg_mod_en = (data[0] & 0x04); uint8_t fdp_conf = (data[0] & 0x03); switch (mirror_conf) { case 0: PrintAndLogEx(INFO, " - no ASCII mirror"); break; case 1: PrintAndLogEx(INFO, " - UID ASCII mirror"); break; case 2: PrintAndLogEx(INFO, " - NFC counter ASCII mirror"); break; case 3: PrintAndLogEx(INFO, " - UID and NFC counter ASCII mirror"); break; default: break; } PrintAndLogEx(INFO, " - SLEEP mode %s", (sleep_en) ? "enabled" : "disabled"); switch (fdp_conf) { case 0: PrintAndLogEx(INFO, " - no field detect"); break; case 1: PrintAndLogEx(INFO, " - enabled by first State-of-Frame (start of communication)"); break; case 2: PrintAndLogEx(INFO, " - enabled by selection of the tag"); break; case 3: PrintAndLogEx(INFO, " - enabled by field presence"); break; default: break; } // valid mirror start page and byte position within start page. if ((tagtype & NTAG_213_F) || (tagtype & NTAG_213_TT)) { switch (mirror_conf) { case 1: { PrintAndLogEx(INFO, " mirror start block %02X | byte pos %02X - %s", data[2], mirror_byte, (data[2] >= 0x4 && data[2] <= 0x24) ? "OK" : "Invalid value"); break;} case 2: { PrintAndLogEx(INFO, " mirror start block %02X | byte pos %02X - %s", data[2], mirror_byte, (data[2] >= 0x4 && data[2] <= 0x26) ? "OK" : "Invalid value"); break;} case 3: { PrintAndLogEx(INFO, " mirror start block %02X | byte pos %02X - %s", data[2], mirror_byte, (data[2] >= 0x4 && data[2] <= 0x22) ? "OK" : "Invalid value"); break;} default: break; } } else if (tagtype & NTAG_216_F) { switch (mirror_conf) { case 1: { PrintAndLogEx(INFO, " mirror start block %02X | byte pos %02X - %s", data[2], mirror_byte, (data[2] >= 0x4 && data[2] <= 0xDE) ? "OK" : "Invalid value"); break;} case 2: { PrintAndLogEx(INFO, " mirror start block %02X | byte pos %02X - %s", data[2], mirror_byte, (data[2] >= 0x4 && data[2] <= 0xE0) ? "OK" : "Invalid value"); break;} case 3: { PrintAndLogEx(INFO, " mirror start block %02X | byte pos %02X - %s", data[2], mirror_byte, (data[2] >= 0x4 && data[2] <= 0xDC) ? "OK" : "Invalid value"); break;} default: break; } } } PrintAndLogEx(INFO, " - strong modulation mode %s", (strg_mod_en) ? "enabled" : "disabled"); if (data[3] < 0xff) PrintAndLogEx(INFO, " - page %d and above need authentication", data[3]); else PrintAndLogEx(INFO, " - pages don't need authentication"); PrintAndLogEx(INFO, " cfg1 [%u/0x%02X]: %s", startPage + 1, startPage + 1, sprint_hex(data + 4, 4)); if (authlim == 0) PrintAndLogEx(INFO, " - " _GREEN_("Unlimited password attempts")); else PrintAndLogEx(INFO, " - Max number of password attempts is " _YELLOW_("%d"), authlim); PrintAndLogEx(INFO, " - NFC counter %s", (nfc_cnf_en) ? "enabled" : "disabled"); PrintAndLogEx(INFO, " - NFC counter %s", (nfc_cnf_prot_pwd) ? "password protection enabled" : "not protected"); PrintAndLogEx(INFO, " - user configuration %s", cfglck ? "permanently locked" : "writeable"); PrintAndLogEx(INFO, " - %s access is protected with password", prot ? "read and write" : "write"); PrintAndLogEx(INFO, " - %02X, Virtual Card Type Identifier is %sdefault", vctid, (vctid == 0x05) ? "" : "not "); PrintAndLogEx(INFO, " PWD [%u/0x%02X]: %s- (cannot be read)", startPage + 2, startPage + 2, sprint_hex(data + 8, 4)); PrintAndLogEx(INFO, " PACK [%u/0x%02X]: %s - (cannot be read)", startPage + 3, startPage + 3, sprint_hex(data + 12, 2)); PrintAndLogEx(INFO, " RFU [%u/0x%02X]: %s- (cannot be read)", startPage + 3, startPage + 3, sprint_hex(data + 14, 2)); return PM3_SUCCESS; } static int ulev1_print_counters(void) { PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "--- " _CYAN_("Tag Counters")); uint8_t tear[1] = {0}; uint8_t counter[3] = {0, 0, 0}; int len = 0; for (uint8_t i = 0; i < 3; ++i) { ulev1_readTearing(i, tear, sizeof(tear)); len = ulev1_readCounter(i, counter, sizeof(counter)); if (len == 3) { PrintAndLogEx(INFO, " [%0d]: %s", i, sprint_hex(counter, 3)); PrintAndLogEx(SUCCESS, " - %02X tearing ( %s )" , tear[0] , (tear[0] == 0xBD) ? _GREEN_("ok") : _RED_("fail") ); } } return len; } static int ulev1_print_signature(TagTypeUL_t tagtype, uint8_t *uid, uint8_t *signature, size_t signature_len) { #define PUBLIC_ECDA_KEYLEN 33 // known public keys for the originality check (source: https://github.com/alexbatalov/node-nxp-originality-verifier) // ref: AN11350 NTAG 21x Originality Signature Validation // ref: AN11341 MIFARE Ultralight EV1 Originality Signature Validation const ecdsa_publickey_t nxp_mfu_public_keys[] = { {"NXP MIFARE Classic MFC1C14_x", "044F6D3F294DEA5737F0F46FFEE88A356EED95695DD7E0C27A591E6F6F65962BAF"}, {"Manufacturer MIFARE Classic MFC1C14_x", "046F70AC557F5461CE5052C8E4A7838C11C7A236797E8A0730A101837C004039C2"}, {"NXP ICODE DNA, ICODE SLIX2", "048878A2A2D3EEC336B4F261A082BD71F9BE11C4E2E896648B32EFA59CEA6E59F0"}, {"NXP Public key", "04A748B6A632FBEE2C0897702B33BEA1C074998E17B84ACA04FF267E5D2C91F6DC"}, {"NXP Ultralight Ev1", "0490933BDCD6E99B4E255E3DA55389A827564E11718E017292FAF23226A96614B8"}, {"NXP NTAG21x (2013)", "04494E1A386D3D3CFE3DC10E5DE68A499B1C202DB5B132393E89ED19FE5BE8BC61"}, {"MIKRON Public key", "04f971eda742a4a80d32dcf6a814a707cc3dc396d35902f72929fdcd698b3468f2"}, }; /* uint8_t nxp_mfu_public_keys[6][PUBLIC_ECDA_KEYLEN] = { // UL, NTAG21x and NDEF { 0x04, 0x49, 0x4e, 0x1a, 0x38, 0x6d, 0x3d, 0x3c, 0xfe, 0x3d, 0xc1, 0x0e, 0x5d, 0xe6, 0x8a, 0x49, 0x9b, 0x1c, 0x20, 0x2d, 0xb5, 0xb1, 0x32, 0x39, 0x3e, 0x89, 0xed, 0x19, 0xfe, 0x5b, 0xe8, 0xbc, 0x61 }, // UL EV1 { 0x04, 0x90, 0x93, 0x3b, 0xdc, 0xd6, 0xe9, 0x9b, 0x4e, 0x25, 0x5e, 0x3d, 0xa5, 0x53, 0x89, 0xa8, 0x27, 0x56, 0x4e, 0x11, 0x71, 0x8e, 0x01, 0x72, 0x92, 0xfa, 0xf2, 0x32, 0x26, 0xa9, 0x66, 0x14, 0xb8 }, // unknown. Needs identification { 0x04, 0x4F, 0x6D, 0x3F, 0x29, 0x4D, 0xEA, 0x57, 0x37, 0xF0, 0xF4, 0x6F, 0xFE, 0xE8, 0x8A, 0x35, 0x6E, 0xED, 0x95, 0x69, 0x5D, 0xD7, 0xE0, 0xC2, 0x7A, 0x59, 0x1E, 0x6F, 0x6F, 0x65, 0x96, 0x2B, 0xAF }, // unknown. Needs identification { 0x04, 0xA7, 0x48, 0xB6, 0xA6, 0x32, 0xFB, 0xEE, 0x2C, 0x08, 0x97, 0x70, 0x2B, 0x33, 0xBE, 0xA1, 0xC0, 0x74, 0x99, 0x8E, 0x17, 0xB8, 0x4A, 0xCA, 0x04, 0xFF, 0x26, 0x7E, 0x5D, 0x2C, 0x91, 0xF6, 0xDC }, // manufacturer public key { 0x04, 0x6F, 0x70, 0xAC, 0x55, 0x7F, 0x54, 0x61, 0xCE, 0x50, 0x52, 0xC8, 0xE4, 0xA7, 0x83, 0x8C, 0x11, 0xC7, 0xA2, 0x36, 0x79, 0x7E, 0x8A, 0x07, 0x30, 0xA1, 0x01, 0x83, 0x7C, 0x00, 0x40, 0x39, 0xC2 }, // MIKRON public key. { 0x04, 0xf9, 0x71, 0xed, 0xa7, 0x42, 0xa4, 0xa8, 0x0d, 0x32, 0xdc, 0xf6, 0xa8, 0x14, 0xa7, 0x07, 0xcc, 0x3d, 0xc3, 0x96, 0xd3, 0x59, 0x02, 0xf7, 0x29, 0x29, 0xfd, 0xcd, 0x69, 0x8b, 0x34, 0x68, 0xf2 } }; */ uint8_t i; bool is_valid = false; for (i = 0; i < ARRAYLEN(nxp_mfu_public_keys); i++) { int dl = 0; uint8_t key[PUBLIC_ECDA_KEYLEN] = {0}; param_gethex_to_eol(nxp_mfu_public_keys[i].value, 0, key, PUBLIC_ECDA_KEYLEN, &dl); int res = ecdsa_signature_r_s_verify(MBEDTLS_ECP_DP_SECP128R1, key, uid, 7, signature, signature_len, false); is_valid = (res == 0); if (is_valid) break; } PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "--- " _CYAN_("Tag Signature")); if (is_valid == false || i == ARRAYLEN(nxp_mfu_public_keys)) { PrintAndLogEx(INFO, " Elliptic curve parameters: NID_secp128r1"); PrintAndLogEx(INFO, " TAG IC Signature: %s", sprint_hex_inrow(signature, signature_len)); PrintAndLogEx(SUCCESS, " Signature verification ( " _RED_("fail") " )"); return PM3_ESOFT; } PrintAndLogEx(INFO, " IC signature public key name: %s", nxp_mfu_public_keys[i].desc); PrintAndLogEx(INFO, "IC signature public key value: %s", nxp_mfu_public_keys[i].value); PrintAndLogEx(INFO, " Elliptic curve parameters: NID_secp128r1"); PrintAndLogEx(INFO, " TAG IC Signature: %s", sprint_hex_inrow(signature, signature_len)); PrintAndLogEx(SUCCESS, " Signature verification ( " _GREEN_("successful") " )"); return PM3_SUCCESS; } static int ulev1_print_version(uint8_t *data) { PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "--- " _CYAN_("Tag Version")); PrintAndLogEx(INFO, " Raw bytes: %s", sprint_hex(data, 8)); PrintAndLogEx(INFO, " Vendor ID: %02X, %s", data[1], getTagInfo(data[1])); PrintAndLogEx(INFO, " Product type: %s", getProductTypeStr(data[2])); PrintAndLogEx(INFO, " Product subtype: %02X, %s", data[3], (data[3] == 1) ? "17 pF" : "50pF"); PrintAndLogEx(INFO, " Major version: %02X", data[4]); PrintAndLogEx(INFO, " Minor version: %02X", data[5]); PrintAndLogEx(INFO, " Size: %s", getUlev1CardSizeStr(data[6])); PrintAndLogEx(INFO, " Protocol type: %02X%s", data[7], (data[7] == 0x3) ? ", ISO14443-3 Compliant" : ""); return PM3_SUCCESS; } static int ntag_print_counter(void) { // NTAG has one counter/tearing. At address 0x02. 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; } /* static int ulc_magic_test(){ // Magic Ultralight test // Magic UL-C, by observation, // 1) it seems to have a static nonce response to 0x1A command. // 2) the deskey bytes is not-zero:d out on as datasheet states. // 3) UID - changeable, not only, but pages 0-1-2-3. // 4) use the ul_magic_test ! magic tags answers specially! int returnValue = UL_ERROR; iso14a_card_select_t card; uint8_t nonce1[11] = {0x00}; uint8_t nonce2[11] = {0x00}; if ( !ul_select(&card) ){ return UL_ERROR; } int status = ulc_requestAuthentication(nonce1, sizeof(nonce1)); if ( status > 0 ) { status = ulc_requestAuthentication(nonce2, sizeof(nonce2)); returnValue = ( !memcmp(nonce1, nonce2, 11) ) ? UL_C_MAGIC : UL_C; } else { returnValue = UL; } DropField(); return returnValue; } */ static int ul_magic_test(void) { // Magic Ultralight tests // 1) take present UID, and try to write it back. OBSOLETE // 2) make a wrong length write to page0, and see if tag answers with ACK/NACK: iso14a_card_select_t card; if (ul_select(&card) == false) return UL_ERROR; int status = ul_comp_write(0, NULL, 0); DropField(); if (status == 0) return MAGIC; // check for GEN1A, GEN1B and NTAG21x uint8_t is_generation = 0; PacketResponseNG resp; clearCommandBuffer(); uint8_t payload[] = { 0 }; SendCommandNG(CMD_HF_MIFARE_CIDENT, payload, sizeof(payload)); if (WaitForResponseTimeout(CMD_HF_MIFARE_CIDENT, &resp, 1500)) { if (resp.status == PM3_SUCCESS) is_generation = resp.data.asBytes[0]; } switch (is_generation) { case MAGIC_GEN_1A: return MAGIC_1A; case MAGIC_GEN_1B: return MAGIC_1B; case MAGIC_NTAG21X: return MAGIC_NTAG; default: break; } return 0; } static char *GenerateFilename(const char *prefix, const char *suffix) { iso14a_card_select_t card; if (ul_select(&card) == false) { PrintAndLogEx(WARNING, "No tag found."); return NULL; } char *fptr = calloc(sizeof(char) * (strlen(prefix) + strlen(suffix)) + sizeof(card.uid) * 2 + 1, sizeof(uint8_t)); strcpy(fptr, prefix); FillFileNameByUID(fptr, card.uid, suffix, card.uidlen); return fptr; } //------------------------------------ /* static int mfu_decrypt_amiibo(uint8_t *encrypted, uint16_t elen, uint8_t *decrypted, uint16_t *dlen) { if (elen < NFC3D_AMIIBO_SIZE / 4) { PrintAndLogEx(ERR, "ERR, data wrong length, got %zu , expected %zu", elen, (NFC3D_AMIIBO_SIZE / 4)); return PM3_ESOFT; } nfc3d_amiibo_keys amiibo_keys = {0}; if (nfc3d_amiibo_load_keys(&amiibo_keys) == false) { return PM3_ESOFT; } if (nfc3d_amiibo_unpack(&amiibo_keys, encrypted, decrypted) == false) { PrintAndLogEx(ERR, "WARNING, Tag signature was NOT valid"); } *dlen = NFC3D_AMIIBO_SIZE; return PM3_SUCCESS; } static int mfu_dump_tag(uint16_t pages, void **pdata, uint16_t *len) { int res = PM3_SUCCESS; uint16_t maxbytes = (pages * 4); *pdata = calloc(maxbytes, sizeof(uint8_t)); if (*pdata == NULL) { PrintAndLogEx(FAILED, "error, cannot allocate memory"); res = PM3_EMALLOC; goto out; } clearCommandBuffer(); SendCommandMIX(CMD_HF_MIFAREU_READCARD, 0, pages, 0, NULL, 0); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) { PrintAndLogEx(WARNING, "Command execute time-out"); free(*pdata); res = PM3_ETIMEOUT; goto out; } if (resp.oldarg[0] != 1) { PrintAndLogEx(WARNING, "Failed reading card"); free(*pdata); res = PM3_ESOFT; goto out; } // read all memory uint32_t startindex = resp.oldarg[2]; uint32_t buffer_size = resp.oldarg[1]; if (buffer_size > maxbytes) { PrintAndLogEx(FAILED, "Data exceeded buffer size!"); buffer_size = maxbytes; } if (!GetFromDevice(BIG_BUF, *pdata, buffer_size, startindex, NULL, 0, NULL, 2500, false)) { PrintAndLogEx(WARNING, "command execution time out"); free(*pdata); res = PM3_ETIMEOUT; goto out; } if (len) *len = buffer_size; out: return res; } */ /* Lego Dimensions, Version: 00 04 04 02 01 00 0F 03 matching bytes: index 12 ( 3 * 4 ) E1 10 12 00 01 03 A0 0C 34 03 13 D1 01 0F 54 02 65 6E */ typedef struct { const char *desc; const char *version; uint8_t mpos; uint8_t mlen; const char *match; uint32_t (*Pwd)(uint8_t *uid); uint16_t (*Pack)(uint8_t *uid); const char *hint; } PACKED mfu_identify_t; static mfu_identify_t mfu_ident_table[] = { { "Jooki", "0004040201000F03", 12, 32, "E11012000103A00C340329D101255504732E6A6F6F6B692E726F636B732F732F", ul_ev1_pwdgen_def, ul_ev1_packgen_def, "hf jooki decode -r" }, { "Lego Dimensions", "0004040201000F03", 12, 18, "E11012000103A00C340313D1010F5402656E", ul_ev1_pwdgenC, ul_ev1_packgenC, "hf mfu dump -k %08x" }, { "Hotwheels", "0004040201000F03", 9, 9, "E110120F", ul_ev1_pwdgen_def, ul_ev1_packgen_def, "hf mfu dump -k %08x" }, { "Minecraft Earth", "0004040201000F03", 9, 26, "48F6FFE1101200037C91012C55027069642E6D617474656C2F4167", ul_ev1_pwdgen_def, ul_ev1_packgen_def, "hf mfu dump -k %08x" }, { "Snackworld", "0004040101000B03", 9, 7, "483000E1100600", NULL, NULL, "hf mfu dump -k %08x" }, { "Amiibo", "0004040201001103", 9, 9, "480FE0F110FFEEA500", ul_ev1_pwdgenB, ul_ev1_packgenB, "hf mfu dump -k %08x" }, {NULL, NULL, 0, 0, NULL, NULL, NULL, NULL} }; static mfu_identify_t *mfu_match_fingerprint(uint8_t *version, uint8_t *data) { uint8_t i = 0; do { int vl = 0; uint8_t vtmp[10] = {0}; param_gethex_to_eol(mfu_ident_table[i].version, 0, vtmp, sizeof(vtmp), &vl); bool m1 = (memcmp(vtmp, version, vl) == 0); if (m1 == false) { PrintAndLogEx(DEBUG, "(fingerprint) wrong version"); continue; } int ml = 0; uint8_t mtmp[40] = {0}; param_gethex_to_eol(mfu_ident_table[i].match, 0, mtmp, sizeof(mtmp), &ml); bool m2 = (memcmp(mtmp, data + mfu_ident_table[i].mpos, mfu_ident_table[i].mlen) == 0); if (m2) { PrintAndLogEx(DEBUG, "(fingerprint) found %s", mfu_ident_table[i].desc); return &mfu_ident_table[i]; } } while (mfu_ident_table[++i].desc); return NULL; } static uint8_t mfu_max_len(void) { uint8_t n = 0, i = 0; do { uint8_t tmp = mfu_ident_table[i].mpos + mfu_ident_table[i].mlen; if (tmp > n) { n = tmp; } } while (mfu_ident_table[++i].desc); return n; } static int mfu_get_version_uid(uint8_t *version, uint8_t *uid) { iso14a_card_select_t card; if (ul_select(&card) == false) return PM3_ESOFT; uint8_t v[10] = {0x00}; int len = ulev1_getVersion(v, sizeof(v)); DropField(); if (len != sizeof(v)) return PM3_ESOFT; memcpy(version, v, 8); memcpy(uid, card.uid, 7); return PM3_SUCCESS; } static int mfu_fingerprint(void) { uint8_t *data = NULL; int res = PM3_SUCCESS; PrintAndLogEx(INFO, "------------------------ " _CYAN_("Fingerprint") " -----------------------"); uint8_t maxbytes = mfu_max_len(); if (maxbytes == 0) { PrintAndLogEx(ERR, "fingerprint table wrong"); res = PM3_ESOFT; goto out; } maxbytes = ((maxbytes / 4) + 1) * 4; data = calloc(maxbytes, sizeof(uint8_t)); if (data == NULL) { PrintAndLogEx(ERR, "failed to allocate memory"); res = PM3_EMALLOC; goto out; } uint8_t pages = (maxbytes / 4); PrintAndLogEx(INFO, "Reading tag memory..."); clearCommandBuffer(); SendCommandMIX(CMD_HF_MIFAREU_READCARD, 0, pages, 0, NULL, 0); PacketResponseNG resp; if (!WaitForResponseTimeout(CMD_ACK, &resp, 2500)) { PrintAndLogEx(WARNING, "Command execute time-out"); res = PM3_ETIMEOUT; goto out; } if (resp.oldarg[0] != 1) { PrintAndLogEx(WARNING, "Failed reading card"); res = PM3_ESOFT; goto out; } // read all memory uint32_t startindex = resp.oldarg[2]; uint32_t buffer_size = resp.oldarg[1]; if (buffer_size > maxbytes) { PrintAndLogEx(FAILED, "Data exceeded buffer size!"); buffer_size = maxbytes; } if (!GetFromDevice(BIG_BUF, data, buffer_size, startindex, NULL, 0, NULL, 2500, false)) { PrintAndLogEx(WARNING, "command execution time out"); res = PM3_ETIMEOUT; goto out; } uint8_t version[8] = {0}; uint8_t uid[7] = {0}; if (mfu_get_version_uid(version, uid) == PM3_SUCCESS) { mfu_identify_t *item = mfu_match_fingerprint(version, data); if (item) { PrintAndLogEx(SUCCESS, "Found " _GREEN_("%s"), item->desc); if (item->Pwd) { char s[40] = {0}; sprintf(s, item->hint, item->Pwd(uid)); PrintAndLogEx(HINT, "Use `" _YELLOW_("%s") "`", s); } else { PrintAndLogEx(HINT, "Use `" _YELLOW_("%s") "`", item->hint); } } } out: free(data); PrintAndLogEx(INFO, "------------------------------------------------------------"); return res; } uint32_t GetHF14AMfU_Type(void) { TagTypeUL_t tagtype = UNKNOWN; iso14a_card_select_t card; if (ul_select(&card) == false) return UL_ERROR; // Ultralight - ATQA / SAK if (card.atqa[1] != 0x00 || card.atqa[0] != 0x44 || card.sak != 0x00) { //PrintAndLogEx(NORMAL, "Tag is not Ultralight | NTAG | MY-D [ATQA: %02X %02X SAK: %02X]\n", card.atqa[1], card.atqa[0], card.sak); DropField(); return UL_ERROR; } if (card.uid[0] != 0x05) { uint8_t version[10] = {0x00}; int len = ulev1_getVersion(version, sizeof(version)); DropField(); switch (len) { case 0x0A: { /* MF0UL1001DUx 0004030100000B03 MF0UL1101DUx 0004030101000B03 MF0ULH1101DUx 0004030201000B03 MF0UL1141DUF 0004030301000B03 MF0UL2101Dxy 0004030101000E03 MF0UL2101DUx 0004030201000E03 MF0UL3101DUx 0004030101001103 MF0ULH3101DUx 0004030201001103 MF0UL5101DUx 0004030101001303 NT2L1011F0DUx 0004040101000B03 NT2H1011G0DUD 0004040201000B03 NT2L1211F0DUx 0004040101000E03 NT2H1311G0DUx 0004040201000F03 NT2H1311F0Dxy 0004040401000F03 NT2H1411G0DUx 0004040201011103 NT2H1511G0DUx 0004040201001103 NT2H1511F0Dxy 0004040401001103 NT2H1611G0DUx 0004040201001303 NT2H1611F0Dxy 0004040401001303 NT2H1311C1DTL 0004040201010F03 NT2H1311TTDUx 0004040203000F03 NT3H1101W0FHK 0004040502001303 NT3H1201W0FHK 0004040502001503 NT3H1101W0FHK_Variant 0004040502011303 NT3H1201 0004040502011503 NT3H2111 0004040502021303 NT3H2211 0004040502021503 nhs 0004040600001303 MF0UN0001DUx 0004030102000B03 MF0UNH0001DUx 0004030202000B03 MF0UN1001DUx 0004030103000B03 MF0UNH1001DUx 0004030203000B03 NT2L1001G0DUx 0004040102000B03 NT2H1001G0DUx 0004040202000B03 NT2H1311TTDUx 0004040203000F03 Micron UL 0034210101000E03 */ if (memcmp(version, "\x00\x04\x03\x01\x01\x00\x0B", 7) == 0) { tagtype = UL_EV1_48; break; } else if (memcmp(version, "\x00\x04\x03\x01\x02\x00\x0B", 7) == 0) { tagtype = UL_NANO_40; break; } else if (memcmp(version, "\x00\x04\x03\x02\x01\x00\x0B", 7) == 0) { tagtype = UL_EV1_48; break; } else if (memcmp(version, "\x00\x04\x03\x01\x01\x00\x0E", 7) == 0) { tagtype = UL_EV1_128; break; } else if (memcmp(version, "\x00\x04\x03\x02\x01\x00\x0E", 7) == 0) { tagtype = UL_EV1_128; break; } else if (memcmp(version, "\x00\x34\x21\x01\x01\x00\x0E", 7) == 0) { tagtype = UL_EV1_128; break; } // Mikron JSC Russia EV1 41 pages tag else if (memcmp(version, "\x00\x04\x04\x01\x01\x00\x0B", 7) == 0) { tagtype = NTAG_210; break; } else if (memcmp(version, "\x00\x04\x04\x01\x02\x00\x0B", 7) == 0) { tagtype = NTAG_210u; break; } else if (memcmp(version, "\x00\x04\x04\x01\x01\x00\x0E", 7) == 0) { tagtype = NTAG_212; break; } else if (memcmp(version, "\x00\x04\x04\x02\x01\x00\x0F", 7) == 0) { tagtype = NTAG_213; break; } else if (memcmp(version, "\x00\x04\x04\x02\x01\x01\x0F", 7) == 0) { tagtype = NTAG_213_C; break; } else if (memcmp(version, "\x00\x04\x04\x02\x01\x00\x11", 7) == 0) { tagtype = NTAG_215; break; } else if (memcmp(version, "\x00\x04\x04\x02\x01\x00\x13", 7) == 0) { tagtype = NTAG_216; break; } else if (memcmp(version, "\x00\x04\x04\x04\x01\x00\x0F", 7) == 0) { tagtype = NTAG_213_F; break; } else if (memcmp(version, "\x00\x04\x04\x04\x01\x00\x13", 7) == 0) { tagtype = NTAG_216_F; break; } else if (memcmp(version, "\x00\x04\x04\x02\x03\x00\x0F", 7) == 0) { tagtype = NTAG_213_TT; break; } else if (memcmp(version, "\x00\x04\x04\x05\x02\x01\x13", 7) == 0) { tagtype = NTAG_I2C_1K; break; } else if (memcmp(version, "\x00\x04\x04\x05\x02\x01\x15", 7) == 0) { tagtype = NTAG_I2C_2K; break; } else if (memcmp(version, "\x00\x04\x04\x05\x02\x02\x13", 7) == 0) { tagtype = NTAG_I2C_1K_PLUS; break; } else if (memcmp(version, "\x00\x04\x04\x05\x02\x02\x15", 7) == 0) { tagtype = NTAG_I2C_2K_PLUS; break; } else if (version[2] == 0x04) { tagtype = NTAG; break; } else if (version[2] == 0x03) { tagtype = UL_EV1; } break; } case 0x01: tagtype = UL_C; break; case 0x00: tagtype = UL; break; case -1 : tagtype = (UL | UL_C | NTAG_203); break; // could be UL | UL_C magic tags default : tagtype = UNKNOWN; break; } // UL vs UL-C vs ntag203 test if (tagtype & (UL | UL_C | NTAG_203)) { if (!ul_select(&card)) return UL_ERROR; // do UL_C check first... uint8_t nonce[11] = {0x00}; int status = ulc_requestAuthentication(nonce, sizeof(nonce)); DropField(); if (status > 1) { tagtype = UL_C; } else { // need to re-select after authentication error if (ul_select(&card) == false) return UL_ERROR; uint8_t data[16] = {0x00}; // read page 0x26-0x29 (last valid ntag203 page) status = ul_read(0x26, data, sizeof(data)); if (status <= 1) { tagtype = UL; } else { // read page 0x30 (should error if it is a ntag203) status = ul_read(0x30, data, sizeof(data)); if (status <= 1) { tagtype = NTAG_203; } else { tagtype = UNKNOWN; } } DropField(); } } if (tagtype & UL) { tagtype = ul_fudan_check(); DropField(); } } else { DropField(); // Infinition MY-D tests Exam high nibble uint8_t nib = (card.uid[1] & 0xf0) >> 4; switch (nib) { // case 0: tagtype = SLE66R35E7; break; //or SLE 66R35E7 - mifare compat... should have different sak/atqa for mf 1k case 1: tagtype = MY_D; break; // or SLE 66RxxS ... up to 512 pages of 8 user bytes... case 2: tagtype = (MY_D_NFC); break; // or SLE 66RxxP ... up to 512 pages of 8 user bytes... (or in nfc mode FF pages of 4 bytes) case 3: tagtype = (MY_D_MOVE | MY_D_MOVE_NFC); break; // or SLE 66R01P // 38 pages of 4 bytes //notice: we can not currently distinguish between these two case 7: tagtype = MY_D_MOVE_LEAN; break; // or SLE 66R01L // 16 pages of 4 bytes } } tagtype |= ul_magic_test(); if (tagtype == (UNKNOWN | MAGIC)) { tagtype = (UL_MAGIC); } return tagtype; } // // extended tag information // static int CmdHF14AMfUInfo(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mfu info", "Get info about MIFARE Ultralight Family styled tag.\n" "Sometimes the tags are locked down, and you may need a key to be able to read the information", "hf mfu info\n" "hf mfu info -k AABBCCDD\n" "hf mfu info --key 00112233445566778899AABBCCDDEEFF" ); void *argtable[] = { arg_param_begin, arg_str0("k", "key", "", "key for authentication (UL-C 16 bytes, EV1/NTAG 4 bytes)"), arg_lit0("l", NULL, "swap entered key's endianness"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); int ak_len = 0; uint8_t authenticationkey[16] = {0x00}; CLIGetHexWithReturn(ctx, 1, authenticationkey, &ak_len); bool swap_endian = arg_get_lit(ctx, 2); CLIParserFree(ctx); if (ak_len) { if (ak_len != 16 && ak_len != 4) { PrintAndLogEx(WARNING, "ERROR: Key is incorrect length\n"); return PM3_EINVARG; } } bool has_auth_key = false; if (ak_len > 0) has_auth_key = true; uint8_t authlim = 0xff; uint8_t data[16] = {0x00}; iso14a_card_select_t card; int status; uint8_t *authkeyptr = authenticationkey; uint8_t pwd[4] = {0, 0, 0, 0}; uint8_t *key = pwd; uint8_t pack[4] = {0, 0, 0, 0}; int len; TagTypeUL_t tagtype = GetHF14AMfU_Type(); if (tagtype == UL_ERROR) return PM3_ESOFT; PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "--- " _CYAN_("Tag Information") " --------------------------"); PrintAndLogEx(INFO, "-------------------------------------------------------------"); ul_print_type(tagtype, 6); // Swap endianness if (swap_endian && has_auth_key) { authkeyptr = SwapEndian64(authenticationkey, ak_len, (ak_len == 16) ? 8 : 4); } if (ul_auth_select(&card, tagtype, has_auth_key, authkeyptr, pack, sizeof(pack)) == PM3_ESOFT) { return PM3_ESOFT; } bool locked = false; // read pages 0,1,2,3 (should read 4pages) status = ul_read(0, data, sizeof(data)); if (status == -1) { DropField(); PrintAndLogEx(ERR, "Error: tag didn't answer to READ"); return PM3_ESOFT; } else if (status == 16) { ul_print_default(data, card.uid); ndef_print_CC(data + 12); } else { locked = true; } // UL_C Specific if ((tagtype & UL_C)) { // read pages 0x28, 0x29, 0x2A, 0x2B uint8_t ulc_conf[16] = {0x00}; status = ul_read(0x28, ulc_conf, sizeof(ulc_conf)); if (status == -1) { PrintAndLogEx(ERR, "Error: tag didn't answer to READ UL-C"); DropField(); return PM3_ESOFT; } if (status == 16) ulc_print_configuration(ulc_conf); else locked = true; if ((tagtype & MAGIC)) { //just read key uint8_t ulc_deskey[16] = {0x00}; status = ul_read(0x2C, ulc_deskey, sizeof(ulc_deskey)); if (status == -1) { DropField(); PrintAndLogEx(ERR, "Error: tag didn't answer to READ magic"); return PM3_ESOFT; } if (status == 16) { ulc_print_3deskey(ulc_deskey); } } else { DropField(); // if we called info with key, just return if (has_auth_key) { return PM3_SUCCESS; } // also try to diversify default keys.. look into CmdHF14AMfGenDiverseKeys if (try_default_3des_keys(&key) == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, "Found default 3des key: "); uint8_t keySwap[16]; memcpy(keySwap, SwapEndian64(key, 16, 8), 16); ulc_print_3deskey(keySwap); } return PM3_SUCCESS; } } // do counters and signature first (don't neet auth) // ul counters are different than ntag counters if ((tagtype & (UL_EV1_48 | UL_EV1_128 | UL_EV1))) { if (ulev1_print_counters() != 3) { // failed - re-select if (ul_auth_select(&card, tagtype, has_auth_key, authkeyptr, pack, sizeof(pack)) == PM3_ESOFT) { return PM3_ESOFT; } } } // NTAG counters? if ((tagtype & (NTAG_213 | NTAG_213_F | NTAG_213_C | NTAG_213_TT | NTAG_215 | NTAG_216))) { if (ntag_print_counter()) { // failed - re-select if (ul_auth_select(&card, tagtype, has_auth_key, authkeyptr, pack, sizeof(pack)) == PM3_ESOFT) { return PM3_ESOFT; } } } // Read signature if ((tagtype & (UL_EV1_48 | UL_EV1_128 | UL_EV1 | UL_NANO_40 | NTAG_210u | NTAG_213 | NTAG_213_F | NTAG_213_C | NTAG_213_TT | NTAG_215 | NTAG_216 | NTAG_216_F | NTAG_I2C_1K | NTAG_I2C_2K | NTAG_I2C_1K_PLUS | NTAG_I2C_2K_PLUS))) { uint8_t ulev1_signature[32] = {0x00}; status = ulev1_readSignature(ulev1_signature, sizeof(ulev1_signature)); if (status == -1) { PrintAndLogEx(ERR, "Error: tag didn't answer to READ SIGNATURE"); DropField(); return PM3_ESOFT; } if (status == 32) { ulev1_print_signature(tagtype, card.uid, ulev1_signature, sizeof(ulev1_signature)); } else { // re-select if (ul_auth_select(&card, tagtype, has_auth_key, authkeyptr, pack, sizeof(pack)) == PM3_ESOFT) { return PM3_ESOFT; } } // Get Version uint8_t version[10] = {0x00}; status = ulev1_getVersion(version, sizeof(version)); if (status == -1) { PrintAndLogEx(ERR, "Error: tag didn't answer to GETVERSION"); DropField(); return PM3_ESOFT; } else if (status == 10) { ulev1_print_version(version); } else { locked = true; if (ul_auth_select(&card, tagtype, has_auth_key, authkeyptr, pack, sizeof(pack)) == PM3_ESOFT) { return PM3_ESOFT; } } uint8_t startconfigblock = 0; uint8_t ulev1_conf[16] = {0x00}; // config blocks always are last 4 pages for (uint8_t i = 0; i < ARRAYLEN(UL_TYPES_ARRAY); i++) { if (tagtype & UL_TYPES_ARRAY[i]) { startconfigblock = UL_MEMORY_ARRAY[i] - 3; break; } } if (startconfigblock) { // if we know where the config block is... status = ul_read(startconfigblock, ulev1_conf, sizeof(ulev1_conf)); if (status == -1) { PrintAndLogEx(ERR, "Error: tag didn't answer to READ EV1"); DropField(); return PM3_ESOFT; } else if (status == 16) { // save AUTHENTICATION LIMITS for later: authlim = (ulev1_conf[4] & 0x07); // add pwd / pack if used from cli if (has_auth_key) { memcpy(ulev1_conf + 8, authkeyptr, 4); memcpy(ulev1_conf + 12, pack, 2); } ulev1_print_configuration(tagtype, ulev1_conf, startconfigblock); } } // AUTHLIMIT, (number of failed authentications) // 0 = limitless. // 1-7 = limit. No automatic tries then. // hasAuthKey, if we was called with key, skip test. if (!authlim && !has_auth_key) { PrintAndLogEx(NORMAL, ""); PrintAndLogEx(SUCCESS, "--- " _CYAN_("Known EV1/NTAG passwords")); // test pwd gen A num_to_bytes(ul_ev1_pwdgenA(card.uid), 4, key); len = ulev1_requestAuthentication(key, pack, sizeof(pack)); if (len > -1) { PrintAndLogEx(SUCCESS, "Found default password " _GREEN_("%s") " pack %02X %02X", sprint_hex(key, 4), pack[0], pack[1]); goto out; } if (ul_auth_select(&card, tagtype, has_auth_key, authkeyptr, pack, sizeof(pack)) == PM3_ESOFT) { return PM3_ESOFT; } // test pwd gen B num_to_bytes(ul_ev1_pwdgenB(card.uid), 4, key); len = ulev1_requestAuthentication(key, pack, sizeof(pack)); if (len > -1) { PrintAndLogEx(SUCCESS, "Found default password " _GREEN_("%s") " pack %02X %02X", sprint_hex(key, 4), pack[0], pack[1]); goto out; } if (ul_auth_select(&card, tagtype, has_auth_key, authkeyptr, pack, sizeof(pack)) == PM3_ESOFT) { return PM3_ESOFT; } // test pwd gen C num_to_bytes(ul_ev1_pwdgenC(card.uid), 4, key); len = ulev1_requestAuthentication(key, pack, sizeof(pack)); if (len > -1) { PrintAndLogEx(SUCCESS, "Found default password " _GREEN_("%s") " pack %02X %02X", sprint_hex(key, 4), pack[0], pack[1]); goto out; } if (ul_auth_select(&card, tagtype, has_auth_key, authkeyptr, pack, sizeof(pack)) == PM3_ESOFT) { return PM3_ESOFT; } // test pwd gen D num_to_bytes(ul_ev1_pwdgenD(card.uid), 4, key); len = ulev1_requestAuthentication(key, pack, sizeof(pack)); if (len > -1) { PrintAndLogEx(SUCCESS, "Found default password" _GREEN_("%s") " pack %02X %02X", sprint_hex(key, 4), pack[0], pack[1]); goto out; } if (ul_auth_select(&card, tagtype, has_auth_key, authkeyptr, pack, sizeof(pack)) == PM3_ESOFT) { return PM3_ESOFT; } for (uint8_t i = 0; i < ARRAYLEN(default_pwd_pack); ++i) { key = default_pwd_pack[i]; len = ulev1_requestAuthentication(key, pack, sizeof(pack)); if (len > -1) { PrintAndLogEx(SUCCESS, "Found default password " _GREEN_("%s") " pack %02X %02X", sprint_hex(key, 4), pack[0], pack[1]); break; } else { if (ul_auth_select(&card, tagtype, has_auth_key, authkeyptr, pack, sizeof(pack)) == PM3_ESOFT) { return PM3_ESOFT; } } } if (len < 1) { PrintAndLogEx(WARNING, _YELLOW_("password not known")); PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`hf mfu pwdgen -r`") " to get see known pwd gen algo suggestions"); } } else { PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`hf mfu pwdgen -r`") " to get see known pwd gen algo suggestions"); } } mfu_fingerprint(); out: DropField(); if (locked) { PrintAndLogEx(INFO, "\nTag appears to be locked, try using a key to get more info"); PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`hf mfu pwdgen -r`") " to get see known pwd gen algo suggestions"); } PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; } // // Write Single Block // static int CmdHF14AMfUWrBl(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mfu wrbl", "Write a block. It autodetects card type.", "hf mfu wrbl -b 0 -d 01234567\n" "hf mfu wrbl -b 0 -d 01234567 -k AABBCCDD\n" "hf mfu wrbl -b 0 -d 01234567 -k 00112233445566778899AABBCCDDEEFF" ); void *argtable[] = { arg_param_begin, arg_str0("k", "key", "", "key for authentication (UL-C 16 bytes, EV1/NTAG 4 bytes)"), arg_lit0("l", NULL, "swap entered key's endianness"), arg_int1("b", "block", "", "block number to write"), arg_str1("d", "data", "", "block data (4 or 16 hex bytes, 16 hex bytes will do a compatibility write)"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); int ak_len = 0; uint8_t authenticationkey[16] = {0x00}; CLIGetHexWithReturn(ctx, 1, authenticationkey, &ak_len); bool swap_endian = arg_get_lit(ctx, 2); int blockno = arg_get_int_def(ctx, 3, -1); int datalen = 0; uint8_t data[16] = {0x00}; CLIGetHexWithReturn(ctx, 4, data, &datalen); CLIParserFree(ctx); bool has_auth_key = false; bool has_pwd = false; if (ak_len == 16) { has_auth_key = true; } else if (ak_len == 4) { has_pwd = true; } else if (ak_len != 0) { PrintAndLogEx(WARNING, "ERROR: Key is incorrect length\n"); return PM3_EINVARG; } if (blockno < 0) { PrintAndLogEx(WARNING, "Wrong block number"); return PM3_EINVARG; } if (datalen != 16 && datalen != 4) { PrintAndLogEx(WARNING, "Wrong data length. Expect 16 or 4, got %d", datalen); return PM3_EINVARG; } uint8_t *authKeyPtr = authenticationkey; // starting with getting tagtype TagTypeUL_t tagtype = GetHF14AMfU_Type(); if (tagtype == UL_ERROR) return PM3_ESOFT; uint8_t maxblockno = 0; for (uint8_t idx = 0; idx < ARRAYLEN(UL_TYPES_ARRAY); idx++) { if (tagtype & UL_TYPES_ARRAY[idx]) { maxblockno = UL_MEMORY_ARRAY[idx]; break; } } if (blockno > maxblockno) { PrintAndLogEx(WARNING, "block number too large. Max block is %u/0x%02X \n", maxblockno, maxblockno); return PM3_EINVARG; } // Swap endianness if (swap_endian) { if (has_auth_key) authKeyPtr = SwapEndian64(authenticationkey, 16, 8); if (has_pwd) authKeyPtr = SwapEndian64(authenticationkey, 4, 4); } if (blockno <= 3) PrintAndLogEx(INFO, "Special block: %0d (0x%02X) [ %s]", blockno, blockno, sprint_hex(data, datalen)); else PrintAndLogEx(INFO, "Block: %0d (0x%02X) [ %s]", blockno, blockno, sprint_hex(data, datalen)); if (ak_len) { PrintAndLogEx(INFO, "Using %s " _GREEN_("%s"), (ak_len == 16) ? "3des" : "pwd", sprint_hex(authenticationkey, ak_len)); } //Send write Block // 4 or 16. uint8_t cmddata[32]; memcpy(cmddata, data, datalen); // 0 - no pwd/key, no authentication // 1 - 3des key (16 bytes) // 2 - pwd (4 bytes) uint8_t keytype = 0; size_t cmdlen = datalen; if (has_auth_key) { keytype = 1; memcpy(cmddata + datalen, authKeyPtr, 16); cmdlen += 16; } else if (has_pwd) { keytype = 2; memcpy(cmddata + datalen, authKeyPtr, 4); cmdlen += 4; } clearCommandBuffer(); if (datalen == 16) { SendCommandMIX(CMD_HF_MIFAREU_WRITEBL_COMPAT, blockno, keytype, 0, cmddata, cmdlen); } else { SendCommandMIX(CMD_HF_MIFAREU_WRITEBL, blockno, keytype, 0, cmddata, cmdlen); } PacketResponseNG resp; if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { uint8_t isOK = resp.oldarg[0] & 0xff; PrintAndLogEx(SUCCESS, "isOk:%02x", isOK); } else { PrintAndLogEx(WARNING, "Command execute timeout"); } return PM3_SUCCESS; } // // Read Single Block // static int CmdHF14AMfURdBl(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mfu rdbl", "Read a block and print. It autodetects card type.", "hf mfu rdbl -b 0\n" "hf mfu rdbl -b 0 -k AABBCCDD\n" "hf mfu rdbl -b 0 --key 00112233445566778899AABBCCDDEEFF" ); void *argtable[] = { arg_param_begin, arg_str0("k", "key", "", "key for authentication (UL-C 16 bytes, EV1/NTAG 4 bytes)"), arg_lit0("l", NULL, "swap entered key's endianness"), arg_int1("b", "block", "", "block number to write"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); int ak_len = 0; uint8_t authenticationkey[16] = {0x00}; CLIGetHexWithReturn(ctx, 1, authenticationkey, &ak_len); bool swap_endian = arg_get_lit(ctx, 2); int blockno = arg_get_int_def(ctx, 3, -1); CLIParserFree(ctx); bool has_auth_key = false; bool has_pwd = false; if (ak_len == 16) { has_auth_key = true; } else if (ak_len == 4) { has_pwd = true; } else if (ak_len != 0) { PrintAndLogEx(WARNING, "ERROR: Key is incorrect length\n"); return PM3_EINVARG; } if (blockno < 0) { PrintAndLogEx(WARNING, "Wrong block number"); return PM3_EINVARG; } uint8_t *authKeyPtr = authenticationkey; // start with getting tagtype TagTypeUL_t tagtype = GetHF14AMfU_Type(); if (tagtype == UL_ERROR) return PM3_ESOFT; uint8_t maxblockno = 0; for (uint8_t idx = 0; idx < ARRAYLEN(UL_TYPES_ARRAY); idx++) { if (tagtype & UL_TYPES_ARRAY[idx]) { maxblockno = UL_MEMORY_ARRAY[idx]; break; } } if (blockno > maxblockno) { PrintAndLogEx(WARNING, "block number to large. Max block is %u/0x%02X \n", maxblockno, maxblockno); return PM3_EINVARG; } // Swap endianness if (swap_endian) { if (has_auth_key) authKeyPtr = SwapEndian64(authenticationkey, ak_len, 8); if (has_pwd) authKeyPtr = SwapEndian64(authenticationkey, ak_len, 4); } if (ak_len) { PrintAndLogEx(INFO, "Using %s " _GREEN_("%s"), (ak_len == 16) ? "3des" : "pwd", sprint_hex(authenticationkey, ak_len)); } //Read Block uint8_t keytype = 0; uint8_t datalen = 0; if (has_auth_key) { keytype = 1; datalen = 16; } else if (has_pwd) { keytype = 2; datalen = 4; } clearCommandBuffer(); SendCommandMIX(CMD_HF_MIFAREU_READBL, blockno, keytype, 0, authKeyPtr, datalen); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { uint8_t isOK = resp.oldarg[0] & 0xff; if (isOK) { uint8_t *d = resp.data.asBytes; PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "Block# | Data | Ascii"); PrintAndLogEx(INFO, "-----------------------------"); PrintAndLogEx(INFO, "%02d/0x%02X | %s| %s\n", blockno, blockno, sprint_hex(d, 4), sprint_ascii(d, 4)); } else { PrintAndLogEx(WARNING, "Failed reading block: ( %02x )", isOK); } } else { PrintAndLogEx(WARNING, "Command execute time-out"); } return PM3_SUCCESS; } void printMFUdumpEx(mfu_dump_t *card, uint16_t pages, uint8_t startpage) { PrintAndLogEx(INFO, _CYAN_("MFU dump file information")); PrintAndLogEx(INFO, "-------------------------------------------------------------"); PrintAndLogEx(INFO, " Version | " _YELLOW_("%s"), sprint_hex(card->version, sizeof(card->version))); PrintAndLogEx(INFO, " TBD 0 | %s", sprint_hex(card->tbo, sizeof(card->tbo))); PrintAndLogEx(INFO, " TBD 1 | %s", sprint_hex(card->tbo1, sizeof(card->tbo1))); PrintAndLogEx(INFO, " Signature | %s", sprint_hex(card->signature, sizeof(card->signature))); for (uint8_t i = 0; i < 3; i ++) { PrintAndLogEx(INFO, " Counter %d | %s", i, sprint_hex(card->counter_tearing[i], 3)); PrintAndLogEx(INFO, " Tearing %d | %s", i, sprint_hex(card->counter_tearing[i] + 3, 1)); } PrintAndLogEx(INFO, "Max data page | " _YELLOW_("%d") " (" _YELLOW_("%d") " bytes)", card->pages - 1, card->pages * 4); PrintAndLogEx(INFO, " Header size | %d", MFU_DUMP_PREFIX_LENGTH); PrintAndLogEx(INFO, "-------------------------------------------------------------"); PrintAndLogEx(INFO, "block# | data |lck| ascii"); PrintAndLogEx(INFO, "---------+-------------+---+------"); uint8_t j = 0; bool lckbit = false; uint8_t *data = card->data; uint8_t lockbytes_sta[] = {0, 0}; uint8_t lockbytes_dyn[] = {0, 0, 0}; bool bit_stat[16] = {0}; bool bit_dyn[16] = {0}; // Load static lock bytes. memcpy(lockbytes_sta, data + 10, sizeof(lockbytes_sta)); for (j = 0; j < 16; j++) { bit_stat[j] = lockbytes_sta[j / 8] & (1 << (7 - j % 8)); } // Load dynamic lockbytes if available // TODO -- FIGURE OUT LOCK BYTES FOR TO EV1 and/or NTAG if (pages == 44) { memcpy(lockbytes_dyn, data + (40 * 4), sizeof(lockbytes_dyn)); for (j = 0; j < 16; j++) { bit_dyn[j] = lockbytes_dyn[j / 8] & (1 << (7 - j % 8)); } PrintAndLogEx(INFO, "DYNAMIC LOCK: %s", sprint_hex(lockbytes_dyn, 3)); } for (uint8_t i = 0; i < pages; ++i) { if (i < 3) { PrintAndLogEx(INFO, "%3d/0x%02X | %s| | %s", i + startpage, i + startpage, sprint_hex(data + i * 4, 4), sprint_ascii(data + i * 4, 4)); continue; } switch (i) { case 3: lckbit = bit_stat[4]; break; case 4: lckbit = bit_stat[3]; break; case 5: lckbit = bit_stat[2]; break; case 6: lckbit = bit_stat[1]; break; case 7: lckbit = bit_stat[0]; break; case 8: lckbit = bit_stat[15]; break; case 9: lckbit = bit_stat[14]; break; case 10: lckbit = bit_stat[13]; break; case 11: lckbit = bit_stat[12]; break; case 12: lckbit = bit_stat[11]; break; case 13: lckbit = bit_stat[10]; break; case 14: lckbit = bit_stat[9]; break; case 15: lckbit = bit_stat[8]; break; case 16: case 17: case 18: case 19: lckbit = bit_dyn[6]; break; case 20: case 21: case 22: case 23: lckbit = bit_dyn[5]; break; case 24: case 25: case 26: case 27: lckbit = bit_dyn[4]; break; case 28: case 29: case 30: case 31: lckbit = bit_dyn[2]; break; case 32: case 33: case 34: case 35: lckbit = bit_dyn[1]; break; case 36: case 37: case 38: case 39: lckbit = bit_dyn[0]; break; case 40: lckbit = bit_dyn[12]; break; case 41: lckbit = bit_dyn[11]; break; case 42: lckbit = bit_dyn[10]; break; //auth0 case 43: lckbit = bit_dyn[9]; break; //auth1 default: break; } PrintAndLogEx(INFO, "%3d/0x%02X | %s| %s | %s", i + startpage, i + startpage, sprint_hex(data + i * 4, 4), (lckbit) ? _RED_("1") : "0", sprint_ascii(data + i * 4, 4)); } PrintAndLogEx(INFO, "---------------------------------"); } // // Mifare Ultralight / Ultralight-C / Ultralight-EV1 // Read and Dump Card Contents, using auto detection of tag size. static int CmdHF14AMfUDump(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mfu dump", "Reads all pages from Ultralight, Ultralight-C, Ultralight EV1\n" "NTAG 203, NTAG 210, NTAG 212, NTAG 213, NTAG 215, NTAG 216\n" "and saves data into binary/json files.\n" "It autodetects card type.", "hf mfu dump -f myfile -> dump whole tag, save to `myfile.bin`\n" "hf mfu dump -k AABBCCDD -> dump whole tag using pwd AABBCCDD\n" "hf mfu dump -p 10 -> start at page 10 and dump rest of blocks\n" "hf mfu dump -p 10 -q 2 -> start at page 10 and dump two blocks\n" "hf mfu dump --key 00112233445566778899AABBCCDDEEFF" ); void *argtable[] = { arg_param_begin, arg_str0("f", "file", "", "specify a filename for dump file"), arg_str0("k", "key", "", "key for authentication (UL-C 16 bytes, EV1/NTAG 4 bytes)"), arg_lit0("l", NULL, "swap entered key's endianness"), arg_int0("p", "page", "", "manually set start page number to start from"), arg_int0("q", "qty", "", "manually set number of pages to dump"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); int fnlen = 0; char filename[FILE_PATH_SIZE] = {0}; CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); int ak_len = 0; uint8_t authenticationkey[16] = {0x00}; uint8_t *authKeyPtr = authenticationkey; CLIGetHexWithReturn(ctx, 2, authenticationkey, &ak_len); bool swap_endian = arg_get_lit(ctx, 3); int start_page = arg_get_int_def(ctx, 4, 0); int pages = arg_get_int_def(ctx, 5, 16); CLIParserFree(ctx); bool has_auth_key = false; bool has_pwd = false; if (ak_len == 16) { has_auth_key = true; } else if (ak_len == 4) { has_pwd = true; } else if (ak_len != 0) { PrintAndLogEx(WARNING, "ERROR: Key is incorrect length\n"); return PM3_EINVARG; } bool manual_pages = false; if (start_page > 0) manual_pages = true; if (pages != 16) manual_pages = true; uint8_t card_mem_size = 0; // Swap endianness if (swap_endian) { if (has_auth_key) authKeyPtr = SwapEndian64(authenticationkey, ak_len, 8); if (has_pwd) authKeyPtr = SwapEndian64(authenticationkey, ak_len, 4); } TagTypeUL_t tagtype = GetHF14AMfU_Type(); if (tagtype == UL_ERROR) return PM3_ESOFT; //get number of pages to read if (manual_pages == false) { for (uint8_t idx = 0; idx < ARRAYLEN(UL_TYPES_ARRAY); idx++) { if (tagtype & UL_TYPES_ARRAY[idx]) { //add one as maxblks starts at 0 card_mem_size = pages = UL_MEMORY_ARRAY[idx] + 1; break; } } } ul_print_type(tagtype, 0); PrintAndLogEx(SUCCESS, "Reading tag memory..."); uint8_t keytype = 0; if (has_auth_key) { if (tagtype & UL_C) keytype = 1; //UL_C auth else keytype = 2; //UL_EV1/NTAG auth } clearCommandBuffer(); SendCommandMIX(CMD_HF_MIFAREU_READCARD, start_page, pages, keytype, authKeyPtr, ak_len); PacketResponseNG resp; if (!WaitForResponseTimeout(CMD_ACK, &resp, 2500)) { PrintAndLogEx(WARNING, "Command execute time-out"); return PM3_ETIMEOUT; } if (resp.oldarg[0] != 1) { PrintAndLogEx(WARNING, "Failed dumping card"); return PM3_ESOFT; } // read all memory uint8_t data[1024] = {0x00}; memset(data, 0x00, sizeof(data)); uint32_t startindex = resp.oldarg[2]; uint32_t buffer_size = resp.oldarg[1]; if (buffer_size > sizeof(data)) { PrintAndLogEx(FAILED, "Data exceeded Buffer size!"); buffer_size = sizeof(data); } if (!GetFromDevice(BIG_BUF, data, buffer_size, startindex, NULL, 0, NULL, 2500, false)) { PrintAndLogEx(WARNING, "command execution time out"); return PM3_ETIMEOUT; } bool is_partial = (pages != buffer_size / 4); pages = buffer_size / 4; iso14a_card_select_t card; mfu_dump_t dump_file_data; memset(&dump_file_data, 0, sizeof(dump_file_data)); uint8_t get_version[] = {0, 0, 0, 0, 0, 0, 0, 0}; uint8_t get_counter_tearing[][4] = {{0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}}; uint8_t get_signature[32]; memset(get_signature, 0, sizeof(get_signature)); // not ul_c and not std ul then attempt to collect info like // VERSION, SIGNATURE, COUNTERS, TEARING, PACK, if (!(tagtype & UL_C || tagtype & UL || tagtype & MY_D_MOVE || tagtype & MY_D_MOVE_LEAN)) { //attempt to read pack uint8_t get_pack[] = {0, 0}; if (ul_auth_select(&card, tagtype, true, authKeyPtr, get_pack, sizeof(get_pack)) != PM3_SUCCESS) { //reset pack get_pack[0] = 0; get_pack[1] = 0; } DropField(); // only add pack if not partial read, and complete pages read. if (!is_partial && pages == card_mem_size) { // add pack to block read memcpy(data + (pages * 4) - 4, get_pack, sizeof(get_pack)); } if (has_auth_key) { uint8_t dummy_pack[] = {0, 0}; ul_auth_select(&card, tagtype, has_auth_key, authKeyPtr, dummy_pack, sizeof(dummy_pack)); } else { ul_select(&card); } ulev1_getVersion(get_version, sizeof(get_version)); // ULEV-1 has 3 counters uint8_t n = 0; // NTAG has 1 counter, at 0x02 if ((tagtype & (NTAG_213 | NTAG_213_F | NTAG_213_C | NTAG_213_TT | NTAG_215 | NTAG_216))) { n = 2; } // NTAG can have nfc counter pwd protection enabled for (; n < 3; n++) { if (has_auth_key) { uint8_t dummy_pack[] = {0, 0}; ul_auth_select(&card, tagtype, has_auth_key, authKeyPtr, dummy_pack, sizeof(dummy_pack)); } else { ul_select(&card); } ulev1_readCounter(n, &get_counter_tearing[n][0], 3); if (has_auth_key) { uint8_t dummy_pack[] = {0, 0}; ul_auth_select(&card, tagtype, has_auth_key, authKeyPtr, dummy_pack, sizeof(dummy_pack)); } else { ul_select(&card); } ulev1_readTearing(n, &get_counter_tearing[n][3], 1); } DropField(); if (has_auth_key) { uint8_t dummy_pack[] = {0, 0}; ul_auth_select(&card, tagtype, has_auth_key, authKeyPtr, dummy_pack, sizeof(dummy_pack)); } else ul_select(&card); ulev1_readSignature(get_signature, sizeof(get_signature)); DropField(); } // format and add keys to block dump output // only add keys if not partial read, and complete pages read if (!is_partial && pages == card_mem_size && has_auth_key) { // if we didn't swapendian before - do it now for the sprint_hex call // NOTE: default entry is bigendian (unless swapped), sprint_hex outputs little endian // need to swap to keep it the same if (swap_endian == false) { authKeyPtr = SwapEndian64(authenticationkey, ak_len, (ak_len == 16) ? 8 : 4); } else { authKeyPtr = authenticationkey; } if (tagtype & UL_C) { //add 4 pages memcpy(data + pages * 4, authKeyPtr, ak_len); pages += ak_len / 4; } else { // 2nd page from end memcpy(data + (pages * 4) - 8, authenticationkey, ak_len); } } //add *special* blocks to dump // pack and pwd saved into last pages of dump, if was not partial read dump_file_data.pages = pages - 1; memcpy(dump_file_data.version, get_version, sizeof(dump_file_data.version)); memcpy(dump_file_data.signature, get_signature, sizeof(dump_file_data.signature)); memcpy(dump_file_data.counter_tearing, get_counter_tearing, sizeof(dump_file_data.counter_tearing)); memcpy(dump_file_data.data, data, pages * 4); printMFUdumpEx(&dump_file_data, pages, start_page); // user supplied filename? if (fnlen < 1) { PrintAndLogEx(INFO, "Using UID as filename"); uint8_t uid[7] = {0}; memcpy(uid, (uint8_t *)&dump_file_data.data, 3); memcpy(uid + 3, (uint8_t *)&dump_file_data.data + 4, 4); strcat(filename, "hf-mfu-"); FillFileNameByUID(filename, uid, "-dump", sizeof(uid)); } uint16_t datalen = pages * 4 + MFU_DUMP_PREFIX_LENGTH; saveFile(filename, ".bin", (uint8_t *)&dump_file_data, datalen); saveFileJSON(filename, jsfMfuMemory, (uint8_t *)&dump_file_data, datalen, NULL); if (is_partial) PrintAndLogEx(WARNING, "Partial dump created. (%d of %d blocks)", pages, card_mem_size); return PM3_SUCCESS; } static void wait4response(uint8_t b) { PacketResponseNG resp; if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { uint8_t isOK = resp.oldarg[0] & 0xff; if (!isOK) PrintAndLogEx(WARNING, "failed to write block %d", b); } else { PrintAndLogEx(WARNING, "Command execute timeout"); } } // // Restore dump file onto tag // static int CmdHF14AMfURestore(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mfu restore", "Restore dumpfile onto card.", "hf mfu restore -f myfile -s -> user specified filename and special write\n" "hf mfu restore -f myfile -k AABBCCDD -s -> user specified filename, special write and use key\n" "hf mfu restore -f myfile -k AABBCCDD -ser -> user specified filename, special write, use key, ..." ); void *argtable[] = { arg_param_begin, arg_str1("f", "file", "", "specify a filename to restore"), arg_str0("k", "key", "", "key for authentication (UL-C 16 bytes, EV1/NTAG 4 bytes)"), arg_lit0("l", NULL, "swap entered key's endianness"), arg_lit0("s", NULL, "enable special write UID -MAGIC TAG ONLY-"), arg_lit0("e", NULL, "enable special write version/signature -MAGIC NTAG 21* ONLY-"), arg_lit0("r", NULL, "use the password found in dumpfile to configure tag. requires " _YELLOW_("'-e'") " parameter to work"), arg_lit0("v", "verbose", "verbose"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); int fnlen = 0; char filename[FILE_PATH_SIZE] = {0}; CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); int ak_len = 0; uint8_t authkey[16] = {0x00}; uint8_t *p_authkey = authkey; CLIGetHexWithReturn(ctx, 2, authkey, &ak_len); bool swap_endian = arg_get_lit(ctx, 3); bool write_special = arg_get_lit(ctx, 4); bool write_extra = arg_get_lit(ctx, 5); bool read_key = arg_get_lit(ctx, 6); bool verbose = arg_get_lit(ctx, 7); CLIParserFree(ctx); bool has_key = false; if (ak_len > 0) { if (ak_len != 4 && ak_len != 16) { PrintAndLogEx(ERR, "Wrong key length. expected 4 or 16, got %d", ak_len); return PM3_EINVARG; } else { has_key = true; } } uint8_t *dump = NULL; size_t bytes_read = 0; if (fnlen == 0) { char *fptr = GenerateFilename("hf-mfu-", "-dump.bin"); if (fptr != NULL) { strcpy(filename, fptr); } else { snprintf(filename, sizeof(filename), "dumpdata.bin"); } free(fptr); } if (loadFile_safe(filename, "", (void **)&dump, &bytes_read) != PM3_SUCCESS) { PrintAndLogEx(WARNING, "Could not find file " _YELLOW_("%s"), filename); return PM3_EIO; } if (bytes_read < MFU_DUMP_PREFIX_LENGTH) { PrintAndLogEx(ERR, "Error, dump file is too small"); free(dump); return PM3_ESOFT; } int res = convert_mfu_dump_format(&dump, &bytes_read, verbose); if (res != PM3_SUCCESS) { PrintAndLogEx(FAILED, "Failed convert on load to new Ultralight/NTAG format"); free(dump); return res; } mfu_dump_t *mem = (mfu_dump_t *)dump; uint8_t pages = (bytes_read - MFU_DUMP_PREFIX_LENGTH) / 4; if (pages - 1 != mem->pages) { PrintAndLogEx(ERR, "Error, invalid dump, wrong page count"); free(dump); return PM3_ESOFT; } PrintAndLogEx(INFO, "Restoring " _YELLOW_("%s")" to card", filename); // print dump printMFUdumpEx(mem, pages, 0); // Swap endianness if (swap_endian && has_key) { if (ak_len == 16) p_authkey = SwapEndian64(authkey, ak_len, 8); else p_authkey = SwapEndian64(authkey, ak_len, 4); } uint8_t data[20] = {0}; uint8_t keytype = 0; // set key - only once if (has_key) { keytype = (ak_len == 16) ? 1 : 2; memcpy(data + 4, p_authkey, ak_len); } // write version, signature, pack // only magic NTAG cards if (write_extra) { #define MFU_NTAG_SPECIAL_PWD 0xF0 #define MFU_NTAG_SPECIAL_PACK 0xF1 #define MFU_NTAG_SPECIAL_VERSION 0xFA #define MFU_NTAG_SPECIAL_SIGNATURE 0xF2 // pwd if (has_key || read_key) { memcpy(data, p_authkey, 4); if (read_key) { // try reading key from dump and use. memcpy(data, mem->data + (bytes_read - MFU_DUMP_PREFIX_LENGTH - 8), 4); } PrintAndLogEx(INFO, "special PWD block written 0x%X - %s\n", MFU_NTAG_SPECIAL_PWD, sprint_hex(data, 4)); clearCommandBuffer(); SendCommandMIX(CMD_HF_MIFAREU_WRITEBL, MFU_NTAG_SPECIAL_PWD, keytype, 0, data, sizeof(data)); wait4response(MFU_NTAG_SPECIAL_PWD); // copy the new key keytype = 2; memcpy(authkey, data, 4); memcpy(data + 4, authkey, 4); } // pack memcpy(data, mem->data + (bytes_read - MFU_DUMP_PREFIX_LENGTH - 4), 2); data[2] = 0; data[3] = 0; PrintAndLogEx(INFO, "special PACK block written 0x%X - %s\n", MFU_NTAG_SPECIAL_PACK, sprint_hex(data, 4)); clearCommandBuffer(); SendCommandMIX(CMD_HF_MIFAREU_WRITEBL, MFU_NTAG_SPECIAL_PACK, keytype, 0, data, sizeof(data)); wait4response(MFU_NTAG_SPECIAL_PACK); // Signature for (uint8_t s = MFU_NTAG_SPECIAL_SIGNATURE, i = 0; s < MFU_NTAG_SPECIAL_SIGNATURE + 8; s++, i += 4) { memcpy(data, mem->signature + i, 4); PrintAndLogEx(INFO, "special SIG block written 0x%X - %s\n", s, sprint_hex(data, 4)); clearCommandBuffer(); SendCommandMIX(CMD_HF_MIFAREU_WRITEBL, s, keytype, 0, data, sizeof(data)); wait4response(s); } // Version for (uint8_t s = MFU_NTAG_SPECIAL_VERSION, i = 0; s < MFU_NTAG_SPECIAL_VERSION + 2; s++, i += 4) { memcpy(data, mem->version + i, 4); PrintAndLogEx(INFO, "special VERSION block written 0x%X - %s\n", s, sprint_hex(data, 4)); clearCommandBuffer(); SendCommandMIX(CMD_HF_MIFAREU_WRITEBL, s, keytype, 0, data, sizeof(data)); wait4response(s); } } PrintAndLogEx(INFO, "Restoring data blocks."); PrintAndLogEx(INFO, "." NOLF); // write all other data // Skip block 0,1,2,3 (only magic tags can write to them) // Skip last 5 blocks usually is configuration for (uint8_t b = 4; b < pages - 5; b++) { //Send write Block memcpy(data, mem->data + (b * 4), 4); clearCommandBuffer(); SendCommandMIX(CMD_HF_MIFAREU_WRITEBL, b, keytype, 0, data, sizeof(data)); wait4response(b); PrintAndLogEx(NORMAL, "." NOLF); fflush(stdout); } PrintAndLogEx(NORMAL, ""); // write special data last if (write_special) { PrintAndLogEx(INFO, "Restoring configuration blocks.\n"); PrintAndLogEx(INFO, "authentication with keytype[%x] %s\n", (uint8_t)(keytype & 0xff), sprint_hex(p_authkey, 4)); // otp, uid, lock, cfg1, cfg0, dynlockbits uint8_t blocks[] = {3, 0, 1, 2, pages - 5, pages - 4, pages - 3}; for (uint8_t i = 0; i < ARRAYLEN(blocks); i++) { uint8_t b = blocks[i]; memcpy(data, mem->data + (b * 4), 4); clearCommandBuffer(); SendCommandMIX(CMD_HF_MIFAREU_WRITEBL, b, keytype, 0, data, sizeof(data)); wait4response(b); PrintAndLogEx(INFO, "special block written %u - %s\n", b, sprint_hex(data, 4)); } } DropField(); free(dump); PrintAndLogEx(INFO, "Restore finished"); return PM3_SUCCESS; } // // Load emulator with dump file // static int CmdHF14AMfUeLoad(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mfu eload", "Load emulator memory with data from `filename.eml` dump file\n" "\nSee `script run data_mfu_bin2eml` to convert the .bin to .eml", "hf mfu eload --ul -f hf-mfu-04010203040506.eml\n" "hf mfu eload --ul -f hf-mfu-04010203040506.eml -q 57 -> load 57 blocks from myfile" ); void *argtable[] = { arg_param_begin, arg_str1("f", "file", "", "filename of dump"), arg_lit1(NULL, "ul", "MIFARE Ultralight family"), arg_int0("q", "qty", "", "number of blocks to load from eml file"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); CLIParserFree(ctx); return CmdHF14AMfELoad(Cmd); } // // Simulate tag // static int CmdHF14AMfUSim(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mfu sim", "Simulate MIFARE Ultralight family type based upon\n" "ISO/IEC 14443 type A tag with 4,7 or 10 byte UID\n" "from emulator memory. See `hf mfu eload` first. \n" "See `hf 14a sim -h` to see available types. You want 2 or 7 usually.", "hf mfu sim -t 2 --uid 1122344556677 -> MIFARE Ultralight\n" "hf mfu sim -t 7 --uid 1122344556677 -n 5 -> AMIIBO (NTAG 215), pack 0x8080" ); void *argtable[] = { arg_param_begin, arg_int1("t", "type", "<1-10> ", "Simulation type to use"), arg_str0("u", "uid", "", "4, 7 or 10 byte UID"), arg_int0("n", "num", "", "Exit simulation after blocks have been read by reader. 0 = infinite"), arg_lit0("v", "verbose", "verbose output"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); CLIParserFree(ctx); return CmdHF14ASim(Cmd); } //------------------------------------------------------------------------------- // Ultralight C Methods //------------------------------------------------------------------------------- // Ultralight C Authentication // static int CmdHF14AMfUCAuth(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mfu cauth", "Tests 3DES password on Mifare Ultralight-C tag.\n" "If password is not specified, a set of known defaults will be tested.", "hf mfu cauth\n" "hf mfu cauth --key 000102030405060708090a0b0c0d0e0f" ); void *argtable[] = { arg_param_begin, arg_str0("k", "key", "", "key for authentication (UL-C 16 bytes)"), arg_lit0("l", NULL, "swap entered key's endianness"), arg_lit0("k", NULL, "keep field on (only if a password is provided too)"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); int ak_len = 0; uint8_t authenticationkey[16] = {0x00}; uint8_t *authKeyPtr = authenticationkey; CLIGetHexWithReturn(ctx, 1, authenticationkey, &ak_len); bool swap_endian = arg_get_lit(ctx, 2); bool keep_field_on = arg_get_lit(ctx, 3); CLIParserFree(ctx); if (ak_len != 16 && ak_len != 0) { PrintAndLogEx(WARNING, "ERROR: Key is incorrect length\n"); return PM3_EINVARG; } // Swap endianness if (swap_endian && ak_len) { authKeyPtr = SwapEndian64(authenticationkey, 16, 8); } bool isok = false; // If no hex key is specified, try default keys if (ak_len == 0) { isok = (try_default_3des_keys(&authKeyPtr) == PM3_SUCCESS); } else { // try user-supplied isok = ulc_authentication(authKeyPtr, !keep_field_on); } if (isok) PrintAndLogEx(SUCCESS, "Authentication success. 3des key: " _GREEN_("%s"), sprint_hex_inrow(authKeyPtr, 16)); else PrintAndLogEx(WARNING, "Authentication ( " _RED_("fail") " )"); return PM3_SUCCESS; } /** A test function to validate that the polarssl-function works the same was as the openssl-implementation. Commented out, since it requires openssl static int CmdTestDES(const char * cmd) { uint8_t key[16] = {0x00}; memcpy(key,key3_3des_data,16); DES_cblock RndA, RndB; PrintAndLogEx(NORMAL, "----------OpenSSL DES implementation----------"); { uint8_t e_RndB[8] = {0x00}; unsigned char RndARndB[16] = {0x00}; DES_cblock iv = { 0 }; DES_key_schedule ks1,ks2; DES_cblock key1,key2; memcpy(key,key3_3des_data,16); memcpy(key1,key,8); memcpy(key2,key+8,8); DES_set_key((DES_cblock *)key1,&ks1); DES_set_key((DES_cblock *)key2,&ks2); DES_random_key(&RndA); PrintAndLogEx(NORMAL, " RndA:%s",sprint_hex(RndA, 8)); PrintAndLogEx(NORMAL, " e_RndB:%s",sprint_hex(e_RndB, 8)); //void DES_ede2_cbc_encrypt(const unsigned char *input, // unsigned char *output, long length, DES_key_schedule *ks1, // DES_key_schedule *ks2, DES_cblock *ivec, int enc); DES_ede2_cbc_encrypt(e_RndB,RndB,sizeof(e_RndB),&ks1,&ks2,&iv,0); PrintAndLogEx(NORMAL, " RndB:%s",sprint_hex(RndB, 8)); rol(RndB,8); memcpy(RndARndB,RndA,8); memcpy(RndARndB+8,RndB,8); PrintAndLogEx(NORMAL, " RA+B:%s",sprint_hex(RndARndB, 16)); DES_ede2_cbc_encrypt(RndARndB,RndARndB,sizeof(RndARndB),&ks1,&ks2,&e_RndB,1); PrintAndLogEx(NORMAL, "enc(RA+B):%s",sprint_hex(RndARndB, 16)); } PrintAndLogEx(NORMAL, "----------PolarSSL implementation----------"); { uint8_t random_a[8] = { 0 }; uint8_t enc_random_a[8] = { 0 }; uint8_t random_b[8] = { 0 }; uint8_t enc_random_b[8] = { 0 }; uint8_t random_a_and_b[16] = { 0 }; des3_context ctx = { 0 }; memcpy(random_a, RndA,8); uint8_t output[8] = { 0 }; uint8_t iv[8] = { 0 }; PrintAndLogEx(NORMAL, " RndA :%s",sprint_hex(random_a, 8)); PrintAndLogEx(NORMAL, " e_RndB:%s",sprint_hex(enc_random_b, 8)); des3_set2key_dec(&ctx, key); des3_crypt_cbc(&ctx // des3_context *ctx , DES_DECRYPT // int mode , sizeof(random_b) // size_t length , iv // unsigned char iv[8] , enc_random_b // const unsigned char *input , random_b // unsigned char *output ); PrintAndLogEx(NORMAL, " RndB:%s",sprint_hex(random_b, 8)); rol(random_b,8); memcpy(random_a_and_b ,random_a,8); memcpy(random_a_and_b+8,random_b,8); PrintAndLogEx(NORMAL, " RA+B:%s",sprint_hex(random_a_and_b, 16)); des3_set2key_enc(&ctx, key); des3_crypt_cbc(&ctx // des3_context *ctx , DES_ENCRYPT // int mode , sizeof(random_a_and_b) // size_t length , enc_random_b // unsigned char iv[8] , random_a_and_b // const unsigned char *input , random_a_and_b // unsigned char *output ); PrintAndLogEx(NORMAL, "enc(RA+B):%s",sprint_hex(random_a_and_b, 16)); } return 0; } **/ // // Mifare Ultralight C - Set password // static int CmdHF14AMfUCSetPwd(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mfu setpwd", "Set the 3DES key on MIFARE Ultralight-C tag. ", "hf mfu setpwd --key 000102030405060708090a0b0c0d0e0f" ); void *argtable[] = { arg_param_begin, arg_str0("k", "key", "", "New key (16 bytes)"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); int k_len = 0; uint8_t key[16] = {0x00}; CLIGetHexWithReturn(ctx, 1, key, &k_len); CLIParserFree(ctx); if (k_len != 16) { PrintAndLogEx(WARNING, "Key must be 16 hex bytes"); return PM3_EINVARG; } clearCommandBuffer(); SendCommandMIX(CMD_HF_MIFAREUC_SETPWD, 0, 0, 0, key, sizeof(key)); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { if ((resp.oldarg[0] & 0xff) == 1) { PrintAndLogEx(INFO, "Ultralight-C new key: %s", sprint_hex(key, sizeof(key))); } else { PrintAndLogEx(WARNING, "Failed writing at block %u", (uint8_t)(resp.oldarg[1] & 0xFF)); return PM3_ESOFT; } } else { PrintAndLogEx(WARNING, "command execution time out"); return PM3_ETIMEOUT; } return PM3_SUCCESS; } // // Magic UL / UL-C tags - Set UID // static int CmdHF14AMfUCSetUid(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mfu setuid", "Set uid on MIFARE Ultralight tag.\n" "This only works for `magic Ultralight` tags.", "hf mfu setuid --uid 11223344556677" ); void *argtable[] = { arg_param_begin, arg_str0("u", "uid", "", "new uid (7 bytes)"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); int u_len = 0; uint8_t uid[7] = {0x00}; CLIGetHexWithReturn(ctx, 1, uid, &u_len); CLIParserFree(ctx); if (u_len != 7) { PrintAndLogEx(WARNING, "UID must be 7 hex bytes"); return PM3_EINVARG; } PrintAndLogEx(INFO, "Please ignore possible transient BCC warnings"); // read block2. PacketResponseNG resp; clearCommandBuffer(); SendCommandMIX(CMD_HF_MIFAREU_READBL, 2, 0, 0, NULL, 0); if (!WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { PrintAndLogEx(WARNING, "Command execute timeout"); return PM3_ETIMEOUT; } // save old block2. uint8_t oldblock2[4] = {0x00}; memcpy(resp.data.asBytes, oldblock2, 4); // Enforce bad BCC handling temporarily as BCC will be wrong between // block 1 write and block2 write hf14a_config config; SendCommandNG(CMD_HF_ISO14443A_GET_CONFIG, NULL, 0); if (!WaitForResponseTimeout(CMD_HF_ISO14443A_GET_CONFIG, &resp, 2000)) { PrintAndLogEx(WARNING, "command execution time out"); return PM3_ETIMEOUT; } memcpy(&config, resp.data.asBytes, sizeof(hf14a_config)); int8_t oldconfig_bcc = config.forcebcc; if (oldconfig_bcc != 2) { config.forcebcc = 2; SendCommandNG(CMD_HF_ISO14443A_SET_CONFIG, (uint8_t *)&config, sizeof(hf14a_config)); } // block 0. uint8_t data[4]; data[0] = uid[0]; data[1] = uid[1]; data[2] = uid[2]; data[3] = 0x88 ^ uid[0] ^ uid[1] ^ uid[2]; clearCommandBuffer(); SendCommandMIX(CMD_HF_MIFAREU_WRITEBL, 0, 0, 0, data, sizeof(data)); if (!WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { PrintAndLogEx(WARNING, "Command execute timeout"); return PM3_ETIMEOUT; } // block 1. data[0] = uid[3]; data[1] = uid[4]; data[2] = uid[5]; data[3] = uid[6]; clearCommandBuffer(); SendCommandMIX(CMD_HF_MIFAREU_WRITEBL, 1, 0, 0, data, sizeof(data)); if (!WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { PrintAndLogEx(WARNING, "Command execute timeout"); return PM3_ETIMEOUT; } // block 2. data[0] = uid[3] ^ uid[4] ^ uid[5] ^ uid[6]; data[1] = oldblock2[1]; data[2] = oldblock2[2]; data[3] = oldblock2[3]; clearCommandBuffer(); SendCommandMIX(CMD_HF_MIFAREU_WRITEBL, 2, 0, 0, data, sizeof(data)); if (!WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { PrintAndLogEx(WARNING, "Command execute timeout"); return PM3_ETIMEOUT; } // restore BCC config if (oldconfig_bcc != 2) { config.forcebcc = oldconfig_bcc; SendCommandNG(CMD_HF_ISO14443A_SET_CONFIG, (uint8_t *)&config, sizeof(hf14a_config)); } return PM3_SUCCESS; } static int CmdHF14AMfUGenDiverseKeys(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mfu keygen", "Set the 3DES key on MIFARE Ultralight-C tag. ", "hf mfu keygen -r\n" "hf mfu keygen --uid 11223344556677" ); void *argtable[] = { arg_param_begin, arg_str0("u", "uid", "", "<4|7> hex byte UID"), arg_lit0("r", NULL, "read UID from tag"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); int ulen = 0; uint8_t uid[7]; CLIGetHexWithReturn(ctx, 1, uid, &ulen); bool read_tag = arg_get_lit(ctx, 2); CLIParserFree(ctx); if (read_tag) { // read uid from tag clearCommandBuffer(); SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_NO_RATS, 0, 0, NULL, 0); PacketResponseNG resp; WaitForResponse(CMD_ACK, &resp); iso14a_card_select_t card; memcpy(&card, (iso14a_card_select_t *)resp.data.asBytes, sizeof(iso14a_card_select_t)); uint64_t select_status = resp.oldarg[0]; // 0: couldn't read, // 1: OK, with ATS // 2: OK, no ATS // 3: proprietary Anticollision if (select_status == 0) { PrintAndLogEx(WARNING, "iso14443a card select failed"); return PM3_ESOFT; } if (card.uidlen != 4 && card.uidlen != 7) { PrintAndLogEx(WARNING, "Wrong sized UID, expected 4|7 bytes got %d", card.uidlen); return PM3_ESOFT; } ulen = card.uidlen; memcpy(uid, card.uid, card.uidlen); } else { if (ulen != 4 && ulen != 7) { PrintAndLogEx(ERR, "Must supply 4 or 7 hex byte uid"); return PM3_EINVARG; } } uint8_t iv[8] = { 0x00 }; uint8_t block = 0x01; uint8_t mifarekeyA[] = { 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5 }; uint8_t mifarekeyB[] = { 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5 }; uint8_t dkeyA[8] = { 0x00 }; uint8_t dkeyB[8] = { 0x00 }; uint8_t masterkey[] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff }; uint8_t mix[8] = { 0x00 }; uint8_t divkey[8] = { 0x00 }; memcpy(mix, mifarekeyA, 4); mix[4] = mifarekeyA[4] ^ uid[0]; mix[5] = mifarekeyA[5] ^ uid[1]; mix[6] = block ^ uid[2]; mix[7] = uid[3]; mbedtls_des3_context ctx_des3; mbedtls_des3_set2key_enc(&ctx_des3, masterkey); mbedtls_des3_crypt_cbc(&ctx_des3 // des3_context , MBEDTLS_DES_ENCRYPT // int mode , sizeof(mix) // length , iv // iv[8] , mix // input , divkey // output ); PrintAndLogEx(SUCCESS, "-- 3DES version"); PrintAndLogEx(SUCCESS, "Masterkey......... %s", sprint_hex(masterkey, sizeof(masterkey))); PrintAndLogEx(SUCCESS, "UID............... %s", sprint_hex(uid, ulen)); PrintAndLogEx(SUCCESS, "block............. %0d", block); PrintAndLogEx(SUCCESS, "Mifare key........ %s", sprint_hex(mifarekeyA, sizeof(mifarekeyA))); PrintAndLogEx(SUCCESS, "Message........... %s", sprint_hex(mix, sizeof(mix))); PrintAndLogEx(SUCCESS, "Diversified key... %s", sprint_hex(divkey + 1, 6)); for (int i = 0; i < ARRAYLEN(mifarekeyA); ++i) { dkeyA[i] = (mifarekeyA[i] << 1) & 0xff; dkeyA[6] |= ((mifarekeyA[i] >> 7) & 1) << (i + 1); } for (int i = 0; i < ARRAYLEN(mifarekeyB); ++i) { dkeyB[1] |= ((mifarekeyB[i] >> 7) & 1) << (i + 1); dkeyB[2 + i] = (mifarekeyB[i] << 1) & 0xff; } uint8_t zeros[8] = {0x00}; uint8_t newpwd[8] = {0x00}; uint8_t dmkey[24] = {0x00}; memcpy(dmkey, dkeyA, 8); memcpy(dmkey + 8, dkeyB, 8); memcpy(dmkey + 16, dkeyA, 8); memset(iv, 0x00, 8); mbedtls_des3_set3key_enc(&ctx_des3, dmkey); mbedtls_des3_crypt_cbc(&ctx_des3 // des3_context , MBEDTLS_DES_ENCRYPT // int mode , sizeof(newpwd) // length , iv // iv[8] , zeros // input , newpwd // output ); PrintAndLogEx(SUCCESS, "\n-- DES version"); PrintAndLogEx(SUCCESS, "MIFARE dkeyA...... %s", sprint_hex(dkeyA, sizeof(dkeyA))); PrintAndLogEx(SUCCESS, "MIFARE dkeyB...... %s", sprint_hex(dkeyB, sizeof(dkeyB))); PrintAndLogEx(SUCCESS, "MIFARE ABA........ %s", sprint_hex(dmkey, sizeof(dmkey))); PrintAndLogEx(SUCCESS, "MIFARE PWD........ %s", sprint_hex(newpwd, sizeof(newpwd))); mbedtls_des3_free(&ctx_des3); mbedtls_aes_context ctx_aes; uint8_t aes_iv[16] = { 0x00 }; uint8_t aes_masterkey[] = { 0x00, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F }; uint8_t aes_input[16] = {0x01, 0x04, 0x2A, 0x2E, 0x19, 0x70, 0x1C, 0x80, 0x01, 0x04, 0x2A, 0x2E, 0x19, 0x70, 0x1C, 0x80}; uint8_t aes_output[16] = {0x00}; mbedtls_aes_setkey_enc(&ctx_aes, aes_masterkey, 128); mbedtls_aes_crypt_cbc(&ctx_aes, MBEDTLS_AES_ENCRYPT, 16, aes_iv, aes_input, aes_output); mbedtls_aes_free(&ctx_aes); PrintAndLogEx(SUCCESS, "\n-- AES version"); PrintAndLogEx(SUCCESS, "MIFARE AES mk..... %s", sprint_hex(aes_masterkey, sizeof(aes_masterkey))); PrintAndLogEx(SUCCESS, "MIFARE Div........ %s", sprint_hex(aes_output, sizeof(aes_output))); // next. from the diversify_key method. return PM3_SUCCESS; } static int CmdHF14AMfUPwdGen(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mfu pwdgen", "Generate different passwords from known pwdgen algos", "hf mfu pwdgen -r\n" "hf mfu pwdgen -t\n" "hf mfu pwdgen --uid 11223344556677" ); void *argtable[] = { arg_param_begin, arg_str0("u", "uid", "", "uid (7 bytes)"), arg_lit0("r", NULL, "read uid from tag"), arg_lit0("t", NULL, "selftest"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); int u_len = 0; uint8_t uid[7] = {0x00}; CLIGetHexWithReturn(ctx, 1, uid, &u_len); bool use_tag = arg_get_lit(ctx, 2); bool selftest = arg_get_lit(ctx, 3); CLIParserFree(ctx); if (selftest) return generator_selftest(); if (use_tag) { // read uid from tag int res = ul_read_uid(uid); if (res != PM3_SUCCESS) { return res; } } else { if (u_len != 7) { PrintAndLogEx(WARNING, "Key must be 7 hex bytes"); return PM3_EINVARG; } } PrintAndLogEx(INFO, "---------------------------------"); PrintAndLogEx(INFO, " Using UID : %s", sprint_hex(uid, 7)); PrintAndLogEx(INFO, "---------------------------------"); PrintAndLogEx(INFO, " algo | pwd | pack"); PrintAndLogEx(INFO, "----------------+----------+-----"); PrintAndLogEx(INFO, " EV1 | %08X | %04X", ul_ev1_pwdgenA(uid), ul_ev1_packgenA(uid)); PrintAndLogEx(INFO, " Amiibo | %08X | %04X", ul_ev1_pwdgenB(uid), ul_ev1_packgenB(uid)); PrintAndLogEx(INFO, " Lego Dimension | %08X | %04X", ul_ev1_pwdgenC(uid), ul_ev1_packgenC(uid)); PrintAndLogEx(INFO, " XYZ 3D printer | %08X | %04X", ul_ev1_pwdgenD(uid), ul_ev1_packgenD(uid)); PrintAndLogEx(INFO, "----------------+----------+-----"); PrintAndLogEx(INFO, " Vingcard algo"); PrintAndLogEx(INFO, "---------------------------------"); return PM3_SUCCESS; } // // MFU TearOff against OTP // Moebius et al // static int CmdHF14AMfuOtpTearoff(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mfu otptear", "Tear-off test against OTP block", "hf mfu otptear -b 3\n" "hf mfu otptear -b 3 -i 100 -s 1000\n" "hf mfu otptear -b 3 -i 1 -e 200\n" "hf mfu otptear -b 3 -i 100 -s 200 -e 2500 -d FFFFFFFF -t EEEEEEEE\n" "hf mfu otptear -b 3 -i 100 -s 200 -e 2500 -d FFFFFFFF -t EEEEEEEE -m 00000000 -> quit when OTP is reset" ); void *argtable[] = { arg_param_begin, arg_u64_0("b", "blk", "", "target block (def 8)"), arg_u64_0("i", "inc", "", "increase time steps (def 500 us)"), arg_u64_0("e", "end", "", "end time (def 3000 us)"), arg_u64_0("s", "start", "", "start time (def 0 us)"), arg_str0("d", "data", "", "initialise data before run (4 bytes)"), arg_str0("t", "test", "", "test write data (4 bytes, 00000000 by default)"), arg_str0("m", "match", "", "exit criteria, if block matches this value (4 bytes)"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); uint8_t blockno = arg_get_u32_def(ctx, 1, 8); uint32_t steps = arg_get_u32_def(ctx, 2, 500); uint32_t end = arg_get_u32_def(ctx, 3, 3000); uint32_t start = arg_get_u32_def(ctx, 4, 0); int d_len = 0; uint8_t data[4] = {0x00}; CLIGetHexWithReturn(ctx, 5, data, &d_len); bool use_data = (d_len > 0); int t_len = 0; uint8_t test[4] = {0x00}; CLIGetHexWithReturn(ctx, 6, test, &t_len); int m_len = 0; uint8_t match[4] = {0x00}; CLIGetHexWithReturn(ctx, 7, match, &m_len); bool use_match = (m_len > 0); CLIParserFree(ctx); if (blockno < 2) { PrintAndLogEx(WARNING, "Block number must be larger than 2."); return PM3_EINVARG; } if (end < steps) { PrintAndLogEx(WARNING, "end time smaller than increase value"); return PM3_EINVARG; } if (end > 43000) { PrintAndLogEx(WARNING, "end time - out of 1 .. 43000 range"); return PM3_EINVARG; } if (start > (end - steps)) { PrintAndLogEx(WARNING, "Start time larger than (end time + steps)"); return PM3_EINVARG; } if (d_len && d_len != 4) { PrintAndLogEx(WARNING, "data must be 4 hex bytes"); return PM3_EINVARG; } if (t_len && t_len != 4) { PrintAndLogEx(WARNING, "test data must be 4 hex bytes"); return PM3_EINVARG; } if (m_len && m_len != 4) { PrintAndLogEx(WARNING, "match data must be 4 hex bytes"); return PM3_EINVARG; } uint8_t teardata[4] = {0x00}; memcpy(teardata, test, sizeof(test)); PrintAndLogEx(INFO, "----------------- " _CYAN_("MFU Tear off") " ---------------------"); PrintAndLogEx(INFO, "Starting Tear-off test"); PrintAndLogEx(INFO, "Target block no: %u", blockno); if (use_data) { PrintAndLogEx(INFO, "Target inital block data : %s", sprint_hex_inrow(data, 4)); } PrintAndLogEx(INFO, "Target write block data : %s", sprint_hex_inrow(teardata, 4)); if (use_match) { PrintAndLogEx(INFO, "Target match block data : %s", sprint_hex_inrow(match, 4)); } PrintAndLogEx(INFO, "----------------------------------------------------"); uint8_t isOK; bool lock_on = false; uint8_t pre[4] = {0}; uint8_t post[4] = {0}; uint32_t current = start; int phase_begin_clear = -1; int phase_end_clear = -1; int phase_begin_newwr = -1; int phase_end_newwr = -1; bool skip_phase1 = false; uint8_t retries = 0; uint8_t error_retries = 0; while ((current <= (end - steps)) && (error_retries < 10)) { if (kbd_enter_pressed()) { PrintAndLogEx(WARNING, "\naborted via keyboard!\n"); break; } PrintAndLogEx(INFO, "Using tear-off delay " _GREEN_("%" PRIu32) " us", current); clearCommandBuffer(); PacketResponseNG resp; if (use_data) { SendCommandMIX(CMD_HF_MIFAREU_WRITEBL, blockno, 0, 0, data, d_len); bool got_written = false; if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { isOK = resp.oldarg[0] & 0xff; if (isOK) { got_written = true; } } if (! got_written) { PrintAndLogEx(FAILED, "Failed to write block BEFORE"); error_retries++; continue; // try again } } SendCommandMIX(CMD_HF_MIFAREU_READBL, blockno, 0, 0, NULL, 0); bool got_pre = false; if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { isOK = resp.oldarg[0] & 0xFF; if (isOK) { memcpy(pre, resp.data.asBytes, sizeof(pre)); got_pre = true; } } if (! got_pre) { PrintAndLogEx(FAILED, "Failed to read block BEFORE"); error_retries++; continue; // try again } clearCommandBuffer(); SendCommandMIX(CMD_HF_MFU_OTP_TEAROFF, blockno, current, 0, teardata, sizeof(teardata)); // we be getting ACK that we are silently ignoring here.. if (!WaitForResponseTimeout(CMD_HF_MFU_OTP_TEAROFF, &resp, 2000)) { PrintAndLogEx(WARNING, "Failed"); return PM3_ESOFT; } if (resp.status != PM3_SUCCESS) { PrintAndLogEx(WARNING, "Tear off reporting failure to select tag"); error_retries++; continue; } bool got_post = false; clearCommandBuffer(); SendCommandMIX(CMD_HF_MIFAREU_READBL, blockno, 0, 0, NULL, 0); if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { isOK = resp.oldarg[0] & 0xFF; if (isOK) { memcpy(post, resp.data.asBytes, sizeof(post)); got_post = true; } } if (! got_post) { PrintAndLogEx(FAILED, "Failed to read block BEFORE"); error_retries++; continue; // try again } error_retries = 0; char prestr[20] = {0}; snprintf(prestr, sizeof(prestr), "%s", sprint_hex_inrow(pre, sizeof(pre))); char poststr[20] = {0}; snprintf(poststr, sizeof(poststr), "%s", sprint_hex_inrow(post, sizeof(post))); if (memcmp(pre, post, sizeof(pre)) == 0) { PrintAndLogEx(INFO, "Current : %02d (0x%02X) %s" , blockno , blockno , poststr ); } else { PrintAndLogEx(INFO, _CYAN_("Tear off occurred") " : %02d (0x%02X) %s => " _RED_("%s") , blockno , blockno , prestr , poststr ); lock_on = true; uint32_t post32 = bytes_to_num(post, 4); uint32_t pre32 = bytes_to_num(pre, 4); if ((phase_begin_clear == -1) && (bitcount32(pre32) > bitcount32(post32))) phase_begin_clear = current; if ((phase_begin_clear > -1) && (phase_end_clear == -1) && (bitcount32(post32) == 0)) phase_end_clear = current; if ((current == start) && (phase_end_clear > -1)) skip_phase1 = true; // new write phase must be atleast 100us later.. if (((bitcount32(pre32) == 0) || (phase_end_clear > -1)) && (phase_begin_newwr == -1) && (bitcount32(post32) != 0) && (skip_phase1 || (current > (phase_end_clear + 100)))) phase_begin_newwr = current; if ((phase_begin_newwr > -1) && (phase_end_newwr == -1) && (memcmp(post, teardata, sizeof(teardata)) == 0)) phase_end_newwr = current; } if (use_match && memcmp(post, match, sizeof(post)) == 0) { PrintAndLogEx(SUCCESS, "Block matches stop condition!\n"); break; } /* TEMPORALLY DISABLED uint8_t d0, d1, d2, d3; d0 = *resp.data.asBytes; d1 = *(resp.data.asBytes + 1); d2 = *(resp.data.asBytes + 2); d3 = *(resp.data.asBytes + 3); if ((d0 != 0xFF) || (d1 != 0xFF) || (d2 != 0xFF) || (d3 = ! 0xFF)) { PrintAndLogEx(NORMAL, "---------------------------------"); PrintAndLogEx(NORMAL, " EFFECT AT: %d us", actualTime); PrintAndLogEx(NORMAL, "---------------------------------\n"); } */ if (start != end) { current += steps; } else { if (lock_on == false) { if (++retries == 20) { current++; end++; start++; retries = 0; PrintAndLogEx(INFO, _CYAN_("Retried %u times, increased delay with 1us"), retries); } } } } PrintAndLogEx(INFO, "----------------------------------------------------"); if ((phase_begin_clear > - 1) && (phase_begin_clear != start)) { PrintAndLogEx(INFO, "Erase phase start boundary around " _YELLOW_("%5d") " us", phase_begin_clear); } if ((phase_end_clear > - 1) && (phase_end_clear != start)) { PrintAndLogEx(INFO, "Erase phase end boundary around " _YELLOW_("%5d") " us", phase_end_clear); } if (phase_begin_newwr > - 1) { PrintAndLogEx(INFO, "Write phase start boundary around " _YELLOW_("%5d") " us", phase_begin_newwr); } if (phase_end_newwr > - 1) { PrintAndLogEx(INFO, "Write phase end boundary around " _YELLOW_("%5d") " us", phase_end_newwr); } PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; } /* static int counter_reset_tear(iso14a_card_select_t *card, uint8_t cnt_no) { PrintAndLogEx(INFO, "Reset tear check"); uint8_t cw[6] = { MIFARE_ULEV1_INCR_CNT, cnt_no, 0x00, 0x00, 0x00, 0x00}; uint8_t ct[1] = {0}; uint8_t resp[10] = {0}; if (ul_select(card) == false) { PrintAndLogEx(FAILED, "failed to select card, exiting..."); return PM3_ESOFT; } if (ul_send_cmd_raw(cw, sizeof(cw), resp, sizeof(resp)) < 0) { PrintAndLogEx(FAILED, "failed to write all ZEROS"); return PM3_ESOFT; } if (ulev1_readTearing(cnt_no, ct, sizeof(ct)) < 0) { PrintAndLogEx(FAILED, "AFTER, failed to read ANTITEAR, exiting..."); return PM3_ESOFT; } DropField(); if (ct[0] != 0xBD) { PrintAndLogEx(INFO, "Resetting seem to have failed, WHY!?"); return PM3_ESOFT; } return PM3_SUCCESS; } */ /* static int CmdHF14AMfuEv1CounterTearoff(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mfu countertear", "Tear-off test against a Ev1 counter", "hf mfu countertear\n" "hf mfu countertear -s 200 -l 2500 -> target counter 0, start delay 200\n" "hf mfu countertear -i 2 -s 200 -l 400 -> target counter 0, start delay 200\n" ); void *argtable[] = { arg_param_begin, arg_int0("c", "cnt", "<0,1,2>", "Target this EV1 counter (0,1,2)"), arg_int0("i", "inc", "", "time interval to increase in each iteration - default 10 us"), arg_int0("l", "limit", "", "test upper limit time - default 3000 us"), arg_int0("s", "start", "", "test start time - default 0 us"), arg_int0(NULL, "fix", "", "test fixed loop delay"), arg_str0("x", "hex", NULL, "3 byte hex to increase counter with"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); int interval = 0; int time_limit, start_time = 0; int counter = arg_get_int_def(ctx, 1, 0); int fixed = arg_get_int_def(ctx, 5, -1); if ( fixed == -1 ) { interval = arg_get_int_def(ctx, 2, 10); time_limit = arg_get_int_def(ctx, 3, 3000); start_time = arg_get_int_def(ctx, 4, 0); } else { start_time = fixed; interval = 0; time_limit = fixed; } uint8_t newvalue[5] = {0}; int newvaluelen = 0; CLIGetHexWithReturn(ctx, 6, newvalue, &newvaluelen); CLIParserFree(ctx); // Validations if (start_time > (time_limit - interval)) { PrintAndLogEx(WARNING, "Wrong start time number"); return PM3_EINVARG; } if (time_limit < interval) { PrintAndLogEx(WARNING, "Wrong time limit number"); return PM3_EINVARG; } if (time_limit > 43000) { PrintAndLogEx(WARNING, "You can't set delay out of 1..43000 range!"); return PM3_EINVARG; } uint8_t cnt_no = 0; if (counter < 0 || counter > 2) { PrintAndLogEx(WARNING, "Counter must 0, 1 or 2"); return PM3_EINVARG; } cnt_no = (uint8_t)counter; iso14a_card_select_t card; // reset counter tear counter_reset_tear(&card, cnt_no); if (ul_select(&card) == false) { PrintAndLogEx(INFO, "failed to select card, exiting..."); return PM3_ESOFT; } uint8_t inital_cnt[3] = {0, 0, 0}; int len = ulev1_readCounter(cnt_no, inital_cnt, sizeof(inital_cnt)); if ( len != sizeof(inital_cnt) ) { PrintAndLogEx(WARNING, "failed to read counter"); return PM3_ESOFT; } uint8_t inital_tear[1] = {0}; len = ulev1_readTearing(cnt_no, inital_tear, sizeof(inital_tear)); DropField(); if ( len != sizeof(inital_tear) ) { PrintAndLogEx(WARNING, "failed to read ANTITEAR, exiting... %d", len); return PM3_ESOFT; } uint32_t wr_value = ( newvalue[0] | newvalue[1] << 8 | newvalue[2] << 16 ); uint32_t inital_value = ( inital_cnt[0] | inital_cnt[1] << 8 | inital_cnt[2] << 16 );; PrintAndLogEx(INFO, "----------------- " _CYAN_("MFU Ev1 Counter Tear off") " ---------------------"); PrintAndLogEx(INFO, "Target counter no [ " _GREEN_("%u") " ]", counter); PrintAndLogEx(INFO, " counter value [ " _GREEN_("%s") " ]", sprint_hex_inrow(inital_cnt, sizeof(inital_cnt))); PrintAndLogEx(INFO, " anti-tear value [ " _GREEN_("%02X") " ]", inital_tear[0]); PrintAndLogEx(INFO, " increase value [ " _GREEN_("%s") " ]", sprint_hex_inrow(newvalue, newvaluelen)); PrintAndLogEx(INFO, "----------------------------------------------------"); uint8_t pre_tear = 0, post_tear = 0; uint8_t pre[3] = {0}; uint8_t post[3] = {0}; uint32_t actual_time = start_time; uint32_t a = 0, b = 0; uint32_t loop = 0; uint16_t late = 0; while (actual_time <= (time_limit - interval)) { DropField(); loop++; if (kbd_enter_pressed()) { PrintAndLogEx(INFO, "\naborted via keyboard!\n"); break; } PrintAndLogEx(INPLACE, "Using tear-off delay " _GREEN_("%" PRIu32) " µs (attempt %u)", actual_time, loop); if (ul_select(&card) == false) { PrintAndLogEx(FAILED, "BEFORE, failed to select card, looping..."); continue; } uint8_t cntresp[3] = {0, 0, 0}; int rlen = ulev1_readCounter(cnt_no, cntresp, sizeof(cntresp)); if ( rlen == sizeof(cntresp) ) { memcpy(pre, cntresp, sizeof(pre)); } else { PrintAndLogEx(NORMAL, ""); PrintAndLogEx(FAILED, "BEFORE, failed to read COUNTER, exiting..."); break; } uint8_t tear[1] = {0}; int tlen = ulev1_readTearing(cnt_no, tear, sizeof(tear)); if ( tlen == sizeof(tear) ) { pre_tear = tear[0]; } else { PrintAndLogEx(NORMAL, ""); PrintAndLogEx(FAILED, "BEFORE, failed to read ANTITEAR, exiting... %d", tlen); break; } DropField(); struct p { uint8_t counter; uint32_t tearoff_time; uint8_t value[3]; } PACKED payload; payload.counter = cnt_no; payload.tearoff_time = actual_time; memcpy(payload.value, newvalue, sizeof(payload.value)); clearCommandBuffer(); PacketResponseNG resp; SendCommandNG(CMD_HF_MFU_COUNTER_TEAROFF, (uint8_t*)&payload, sizeof(payload)); if (!WaitForResponseTimeout(CMD_HF_MFU_COUNTER_TEAROFF, &resp, 2000)) { PrintAndLogEx(WARNING, "\ntear off command failed"); continue; } if (ul_select(&card) == false) { PrintAndLogEx(FAILED, "AFTER, failed to select card, exiting..."); break; } rlen = ulev1_readCounter(cnt_no, cntresp, sizeof(cntresp)); if ( rlen == sizeof(cntresp) ) { memcpy(post, cntresp, sizeof(post)); } else { PrintAndLogEx(NORMAL, ""); PrintAndLogEx(FAILED, "AFTER, failed to read COUNTER, exiting..."); break; } tear[0] = 0; tlen = ulev1_readTearing(cnt_no, tear, sizeof(tear)); if ( tlen == sizeof(tear) ) { post_tear = tear[0]; } else { PrintAndLogEx(NORMAL, ""); PrintAndLogEx(FAILED, "AFTER, failed to read ANTITEAR, exiting..."); break; } DropField(); char prestr[20] = {0}; snprintf(prestr, sizeof(prestr), "%s", sprint_hex_inrow(pre, sizeof(pre))); char poststr[20] = {0}; snprintf(poststr, sizeof(poststr), "%s", sprint_hex_inrow(post, sizeof(post))); bool post_tear_check = (post_tear == 0xBD); a = (pre[0] | pre[1] << 8 | pre[2] << 16); b = (post[0] | post[1] << 8 | post[2] << 16); // A != B if (memcmp(pre, post, sizeof(pre)) != 0) { PrintAndLogEx(NORMAL, ""); if (inital_value != a ) { if ( inital_value != b ) PrintAndLogEx(INFO, "pre %08x, post %08x != inital %08x | tear: 0x%02X == 0x%02X", a, b, inital_value, pre_tear, post_tear); else PrintAndLogEx(INFO, "pre %08x != inital and post %08x == inital %08x | tear: 0x%02X == 0x%02X", a, b, inital_value, pre_tear, post_tear); } else { if ( inital_value != b ) PrintAndLogEx(INFO, "pre %08x == inital and post %08x != inital %08x | tear: 0x%02X == 0x%02X", a, b, inital_value, pre_tear, post_tear); } if ( b == 0 ) { PrintAndLogEx(INFO, _CYAN_("Tear off occured (ZEROS value!) -> ") "%s vs " _GREEN_("%s") " Tear status: 0x%02X == 0x%02X ( %s )" , prestr , poststr , pre_tear , post_tear , post_tear_check ? _GREEN_("OK") : _RED_("DETECTED") ); break; } if ( a > b ) { PrintAndLogEx(INFO, _CYAN_("Tear off occured " _RED_("( LESS )") " -> ") "%s vs " _GREEN_("%s") " Tear status: 0x%02X == 0x%02X ( %s )" , prestr , poststr , pre_tear , post_tear , post_tear_check ? _GREEN_("OK") : _RED_("DETECTED") ); if (counter_reset_tear(&card, cnt_no) != PM3_SUCCESS){ PrintAndLogEx(FAILED, "failed to reset tear, exiting..."); break; } uint32_t bar = (0x1000000 - b) + 2; // wr_value = bar; // newvalue[0] = (bar) & 0xFF; // newvalue[1] = ((bar >> 8) & 0xFF); // newvalue[2] = ((bar >> 16) & 0xFF); wr_value = 0; newvalue[0] = 0; newvalue[1] = 0; newvalue[2] = 0; PrintAndLogEx(INFO, " 0x1000000 - 0x%x == 0x%x", b, bar); PrintAndLogEx(INFO, " new increase value 0x%x" , wr_value); PrintAndLogEx(INFO, " because BAR + post == 0x%x" , bar + b); PrintAndLogEx(INFO, "New increase value " _YELLOW_("%s"), sprint_hex_inrow(newvalue, newvaluelen)); continue; } else { PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, _CYAN_("Tear off occured (+1) (too late) -> ") "%s vs %s Tear: 0x%02X == 0x%02X ( %s )" , prestr , poststr , pre_tear , post_tear , post_tear_check ? _GREEN_("OK") : _RED_("DETECTED") ); if ( post_tear_check && b == inital_value) { PrintAndLogEx(INFO, "Reverted to previous value"); break; } if ( wr_value != 0 ) { //uint32_t bar = (0x1000000 - b) + 2; wr_value = 0; newvalue[0] = 0; newvalue[1] = 0; newvalue[2] = 0; if ( b >= (inital_value + (2 * wr_value))) { PrintAndLogEx(INFO, "Large " _YELLOW_("( JUMP )") " detected"); // wr_value = bar; // newvalue[0] = (bar) & 0xFF; // newvalue[1] = ((bar >> 8) & 0xFF); // newvalue[2] = ((bar >> 16) & 0xFF); } else { // wr_value = bar; // newvalue[0] = (bar) & 0xFF; // newvalue[1] = ((bar >> 8) & 0xFF); // newvalue[2] = ((bar >> 16) & 0xFF); // wr_value = 0; // newvalue[0] = 0; // newvalue[1] = 0; // newvalue[2] = 0; } } PrintAndLogEx(INFO, "New increase value " _YELLOW_("%s"), sprint_hex_inrow(newvalue, newvaluelen)); //actual_time--; late++; } } else { PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, _CYAN_("Status: same value! -> ") "%s == %s Tear: 0x%02X == 0x%02X ( %s )" , prestr , poststr , pre_tear , post_tear , post_tear_check ? _GREEN_("OK") : _RED_("DETECTED") ); if ( post_tear_check ) { if ( a == b ) { //actual_time--; continue; } if ( b == inital_value ) { PrintAndLogEx(INFO, "Reverted to previous value"); break; } } else { if (counter_reset_tear(&card, cnt_no) != PM3_SUCCESS){ PrintAndLogEx(FAILED, "failed to reset tear, exiting..."); break; } } } actual_time += interval; } DropField(); PrintAndLogEx(INFO, " Sent %u tear offs ", loop); counter_reset_tear(&card, cnt_no); PrintAndLogEx(INFO, "hf 14a raw -s -c 3900 --> read counter 0"); PrintAndLogEx(INFO, "hf 14a raw -s -c 3e00 --> read tearing 0"); PrintAndLogEx(NORMAL, ""); char read_cnt_str[30]; sprintf(read_cnt_str, "hf 14a raw -s -c 39%02x", counter); CommandReceived(read_cnt_str); char read_tear_str[30]; sprintf(read_tear_str, "hf 14a raw -s -c 3e%02x", counter); CommandReceived(read_tear_str); return PM3_SUCCESS; } */ // // name, identifying bytes, decode function, hints text // identifying bits // 1. getversion data must match. // 2. magic bytes in the readable payload int CmdHF14MfuNDEFRead(const char *Cmd) { int keylen; int maxsize = 16, status; bool hasAuthKey = false; bool swapEndian = false; iso14a_card_select_t card; uint8_t data[16] = {0x00}; uint8_t key[16] = {0x00}; uint8_t *p_key = key; uint8_t pack[4] = {0, 0, 0, 0}; CLIParserContext *ctx; CLIParserInit(&ctx, "hf mfu ndefread", "Prints NFC Data Exchange Format (NDEF)", "hf mfu ndefread -> shows NDEF data\n" "hf mfu ndefread -k ffffffff -> shows NDEF data with key"); void *argtable[] = { arg_param_begin, arg_str0("k", "key", "replace default key for NDEF", NULL), arg_lit0("l", NULL, "swap entered key's endianness"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); CLIGetHexWithReturn(ctx, 1, key, &keylen); swapEndian = arg_get_lit(ctx, 2); CLIParserFree(ctx); switch (keylen) { case 0: break; case 4: case 16: hasAuthKey = true; break; default: PrintAndLogEx(WARNING, "ERROR: Key is incorrect length\n"); return PM3_EINVARG; } // Get tag type TagTypeUL_t tagtype = GetHF14AMfU_Type(); if (tagtype == UL_ERROR) { PrintAndLogEx(WARNING, "No Ultraligth / NTAG based tag found"); return PM3_ESOFT; } // Is tag UL/NTAG? // Swap endianness if (swapEndian && hasAuthKey) p_key = SwapEndian64(key, keylen, (keylen == 16) ? 8 : 4); // Select and Auth if (ul_auth_select(&card, tagtype, hasAuthKey, p_key, pack, sizeof(pack)) == PM3_ESOFT) return PM3_ESOFT; // read pages 0,1,2,3 (should read 4pages) status = ul_read(0, data, sizeof(data)); if (status == -1) { DropField(); PrintAndLogEx(ERR, "Error: tag didn't answer to READ"); return PM3_ESOFT; } else if (status == 16) { status = ndef_print_CC(data + 12); if (status == PM3_ESOFT) { DropField(); PrintAndLogEx(ERR, "Error: tag didn't contain a NDEF Container"); return PM3_ESOFT; } // max datasize; maxsize = ndef_get_maxsize(data + 12); } // iceman: maybe always take MIN of tag identified size vs NDEF reported size? // fix: UL_EV1 48bytes != NDEF reported size for (uint8_t i = 0; i < ARRAYLEN(UL_TYPES_ARRAY); i++) { if (tagtype & UL_TYPES_ARRAY[i]) { if (maxsize != (UL_MEMORY_ARRAY[i] * 4)) { PrintAndLogEx(INFO, "Tag reported size vs NDEF reported size mismatch. Using smallest value"); } maxsize = MIN(maxsize, (UL_MEMORY_ARRAY[i] * 4)); break; } } // allocate mem uint8_t *records = calloc(maxsize, sizeof(uint8_t)); if (records == NULL) { DropField(); return PM3_EMALLOC; } // read NDEF records. for (uint16_t i = 0, j = 0; i < maxsize; i += 16, j += 4) { status = ul_read(4 + j, records + i, 16); if (status == -1) { DropField(); PrintAndLogEx(ERR, "Error: tag didn't answer to READ"); free(records); return PM3_ESOFT; } } DropField(); status = NDEFRecordsDecodeAndPrint(records, (size_t)maxsize); if (status != PM3_SUCCESS) { status = NDEFDecodeAndPrint(records, (size_t)maxsize, true); } char *jooki = strstr((char *)records, "s.jooki.rocks/s/?s="); if (jooki) { jooki += 17; while (jooki) { if ((*jooki) != '=') jooki++; else { jooki++; char s[17] = {0}; strncpy(s, jooki, 16); PrintAndLogEx(HINT, "Use `" _YELLOW_("hf jooki decode -d %s") "` to decode", s); break; } } } char *mattel = strstr((char *)records, ".pid.mattel/"); if (mattel) { mattel += 12; while (mattel) { if ((*mattel) != '/') mattel++; else { mattel++; char b64[33] = {0}; strncpy(b64, mattel, 32); uint8_t arr[24] = {0}; size_t arrlen = 0; mbedtls_base64_decode(arr, sizeof(arr), &arrlen, (const unsigned char *)b64, 32); PrintAndLogEx(INFO, "decoded... %s", sprint_hex(arr, arrlen)); break; } } } free(records); return status; } static int CmdHF14AMfuEView(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mfu eview", "It displays emulator memory", "hf mfu eview" ); void *argtable[] = { arg_param_begin, arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); CLIParserFree(ctx); uint16_t blocks = 0xFF; uint16_t bytes = blocks * 4; uint8_t *dump = calloc(bytes, sizeof(uint8_t)); if (dump == NULL) { PrintAndLogEx(WARNING, "Fail, cannot allocate memory"); return PM3_EMALLOC; } PrintAndLogEx(INFO, "downloading from emulator memory"); if (!GetFromDevice(BIG_BUF_EML, dump, bytes, 0, NULL, 0, NULL, 2500, false)) { PrintAndLogEx(WARNING, "Fail, transfer from device time-out"); free(dump); return PM3_ETIMEOUT; } PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "----+-------------+-------"); PrintAndLogEx(INFO, "blk | data | ascii"); PrintAndLogEx(INFO, "----+-------------+-------"); for (uint16_t i = 0; i < blocks; i++) { PrintAndLogEx(INFO, "%03d | %s ", i, sprint_hex_ascii(dump + (i * 4), 4)); } PrintAndLogEx(INFO, "----+-------------+-------"); PrintAndLogEx(NORMAL, ""); free(dump); return PM3_SUCCESS; } /* static int CmdHF14AMfUCDecryptAmiibo(const char *Cmd){ CLIParserContext *ctx; CLIParserInit(&ctx, "hf mfu decrypt", "Tries to read all memory from amiibo tag and decrypt it", "hf mfu decrypt" ); void *argtable[] = { arg_param_begin, arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); CLIParserFree(ctx); uint16_t elen = 0, dlen = 0; uint8_t *encrypted = NULL; int res = mfu_dump_tag( MAX_NTAG_215, (void **)&encrypted, &elen); if (res == PM3_SUCCESS) { PrintAndLogEx(INFO, "32 first bytes of tag dump"); PrintAndLogEx(INFO, "%s", sprint_hex(encrypted, 32)); PrintAndLogEx(INFO, "-----------------------"); uint8_t decrypted[NFC3D_AMIIBO_SIZE] = {0}; res = mfu_decrypt_amiibo(encrypted, elen, decrypted, &dlen); if ( res == PM3_SUCCESS) { for (uint8_t i = 0; i < dlen/16; i++ ) { PrintAndLogEx(INFO, "[%d] %s", i, sprint_hex_ascii(decrypted + (i * 16), 16)); } } free(encrypted); } return PM3_SUCCESS; } */ //------------------------------------ // Menu Stuff //------------------------------------ static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help"}, {"-----------", CmdHelp, IfPm3Iso14443a, "----------------------- " _CYAN_("recovery") " -------------------------"}, {"keygen", CmdHF14AMfUGenDiverseKeys, AlwaysAvailable, "Generate 3DES MIFARE diversified keys"}, {"pwdgen", CmdHF14AMfUPwdGen, AlwaysAvailable, "Generate pwd from known algos"}, {"otptear", CmdHF14AMfuOtpTearoff, IfPm3Iso14443a, "Tear-off test on OTP bits"}, // {"tear_cnt", CmdHF14AMfuEv1CounterTearoff, IfPm3Iso14443a, "Tear-off test on Ev1/NTAG Counter bits"}, {"-----------", CmdHelp, IfPm3Iso14443a, "----------------------- " _CYAN_("operations") " -----------------------"}, {"cauth", CmdHF14AMfUCAuth, IfPm3Iso14443a, "Authentication - Ultralight-C"}, {"dump", CmdHF14AMfUDump, IfPm3Iso14443a, "Dump MIFARE Ultralight family tag to binary file"}, {"info", CmdHF14AMfUInfo, IfPm3Iso14443a, "Tag information"}, {"ndefread", CmdHF14MfuNDEFRead, IfPm3Iso14443a, "Prints NDEF records from card"}, {"rdbl", CmdHF14AMfURdBl, IfPm3Iso14443a, "Read block"}, {"restore", CmdHF14AMfURestore, IfPm3Iso14443a, "Restore a dump onto a MFU MAGIC tag"}, {"wrbl", CmdHF14AMfUWrBl, IfPm3Iso14443a, "Write block"}, {"---------", CmdHelp, IfPm3Iso14443a, "----------------------- " _CYAN_("simulation") " -----------------------"}, {"eload", CmdHF14AMfUeLoad, IfPm3Iso14443a, "load Ultralight .eml dump file into emulator memory"}, {"eview", CmdHF14AMfuEView, IfPm3Iso14443a, "View emulator memory"}, {"sim", CmdHF14AMfUSim, IfPm3Iso14443a, "Simulate MIFARE Ultralight from emulator memory"}, {"---------", CmdHelp, IfPm3Iso14443a, "----------------------- " _CYAN_("magic") " ----------------------------"}, {"setpwd", CmdHF14AMfUCSetPwd, IfPm3Iso14443a, "Set 3DES key - Ultralight-C"}, {"setuid", CmdHF14AMfUCSetUid, IfPm3Iso14443a, "Set UID - MAGIC tags only"}, // {"---------", CmdHelp, IfPm3Iso14443a, "----------------------- " _CYAN_("amiibo") " ----------------------------"}, // {"decrypt", CmdHF14AMfUCDecryptAmiibo, IfPm3Iso14443a, "Decrypt a amiibo tag"}, {NULL, NULL, NULL, NULL} }; static int CmdHelp(const char *Cmd) { (void)Cmd; // Cmd is not used so far CmdsHelp(CommandTable); return PM3_SUCCESS; } int CmdHFMFUltra(const char *Cmd) { clearCommandBuffer(); return CmdsParse(CommandTable, Cmd); }