reworked and improved the hf mfp chk key handling. reworked the nxp_detect_card technology function to enable other parts of the client to benefit from detecting card types. Like hf mf info or hf mf autopwn - two most common used commands. Now less waiting and more easily to know what next steps to do.

This commit is contained in:
iceman1001 2025-06-09 15:36:44 +02:00
commit 5f2edb9bb8
11 changed files with 996 additions and 443 deletions

View file

@ -246,6 +246,49 @@ static uint16_t gs_frame_len = 0;
static uint8_t gs_frames_num = 0;
static uint16_t atsFSC[] = {16, 24, 32, 40, 48, 64, 96, 128, 256};
int hf14a_getversion_data(iso14a_card_select_t *card, uint64_t select_status, version_hw_t *hw) {
// field on, card selected if select_status is 1 or 4, not selected if 2
int res = PM3_EFAILED;
// if 4b UID or NXP, try to get version
if ((card->uidlen == 4) || ((card->uidlen == 7) && (card->uid[0] == 0x04))) {
// GetVersion
if ((select_status == 1) || (select_status == 4)) { // L4
uint8_t response[PM3_CMD_DATA_SIZE] = {0};
int resp_len = 0;
uint8_t getVersion[5] = {0x90, 0x60, 0x00, 0x00, 0x00};
res = ExchangeAPDU14a(getVersion, sizeof(getVersion), false, false, response, sizeof(response), &resp_len);
DropField();
if (res == PM3_ETIMEOUT) {
PrintAndLogEx(DEBUG, "iso14443a card select timeout");
return PM3_ETIMEOUT;
}
if (resp_len == 9) {
memcpy(hw, response, sizeof(version_hw_t));
return PM3_SUCCESS;
}
return PM3_EFAILED;
}
// select_status = 2, L3
uint8_t version[8] = {0};
uint8_t uid[7] = {0};
res = mfu_get_version_uid(version, uid);
DropField();
if (res == PM3_SUCCESS) {
memcpy(hw, version + 1, sizeof(version_hw_t));
}
}
DropField();
return res;
}
static int CmdHF14AList(const char *Cmd) {
return CmdTraceListAlias(Cmd, "hf 14a", "14a -c");
}
@ -1735,94 +1778,236 @@ static void printTag(const char *tag) {
PrintAndLogEx(SUCCESS, " " _YELLOW_("%s"), tag);
}
int detect_nxp_card(uint8_t sak, uint16_t atqa, uint64_t select_status) {
// Based on NXP AN10833 Rev 3.8 and NXP AN10834 Rev 4.2
int detect_nxp_card(uint8_t sak, uint16_t atqa, uint64_t select_status,
uint8_t ats_hist_len, uint8_t *ats_hist,
bool version_hw_available, version_hw_t *version_hw) {
int type = MTNONE;
if ((sak & 0x02) != 0x02) {
if ((sak & 0x19) == 0x19) {
type |= MTCLASSIC;
} else if ((sak & 0x40) == 0x40) {
type |= MTISO18092;
} else if ((sak & 0x38) == 0x38) {
type |= MTCLASSIC;
} else if ((sak & 0x18) == 0x18) {
if (select_status == 1) {
type |= MTPLUS;
} else {
type |= MTCLASSIC;
}
} else if ((sak & 0x09) == 0x09) {
type |= MTMINI;
} else if ((sak & 0x28) == 0x28) {
type |= MTCLASSIC;
} else if ((sak & 0x08) == 0x08) {
if (select_status == 1) {
type |= MTPLUS;
} else {
type |= MTCLASSIC;
}
} else if ((sak & 0x11) == 0x11) {
type |= MTPLUS;
} else if ((sak & 0x10) == 0x10) {
type |= MTPLUS;
} else if ((sak & 0x01) == 0x01) {
type |= MTCLASSIC;
} else if ((sak & 0x24) == 0x24) {
type |= MTDESFIRE;
} else if ((sak & 0x20) == 0x20) {
if (select_status == 1) {
if ((atqa & 0x0040) == 0x0040) {
if ((atqa & 0x0300) == 0x0300) {
type |= MTDESFIRE;
} else {
type |= MTPLUS;
}
} else {
if (version_hw_available) {
if ((atqa & 0x0001) == 0x0001) {
type |= HID_SEOS;
} else {
type |= MTPLUS;
}
switch (version_hw->product_type & 0x0F) {
case 0x1: {
type |= MTDESFIRE;
if ((atqa & 0x0004) == 0x0004) {
// special cases, override major_product_version_str when needed
switch (version_hw->major_product_version) {
case 0x42:
type |= MTEMV;
break;
case 0xA0:
type |= MTDUOX;
break;
}
break;
}
case 0x2: {
type |= MTPLUS;
break;
}
case 0x3: {
type |= MTULTRALIGHT;
break;
}
case 0x4: {
type |= MTNTAG;
break;
}
case 0x7: {
type |= MTNTAG;
break;
}
case 0x8: {
type |= MTDESFIRE;
break;
}
case 0x9: {
break;
}
default: {
break;
}
}
}
if ((sak & 0x44) == 0x40) {
// ISO18092 Table 15: Target compliant with NFC transport protocol
type |= MTISO18092;
}
if ((sak & 0x02) == 0x00) { // SAK b2=0
if ((sak & 0x08) == 0x08) { // SAK b2=0 b4=1
if ((sak & 0x10) == 0x10) { // SAK b2=0 b4=1 b5=1
if ((sak & 0x01) == 0x01) { // SAK b2=0 b4=1 b5=1 b1=1, SAK=0x19
type |= MTCLASSIC;
} else { // SAK b2=0 b4=1 b5=1 b1=0
if ((sak & 0x20) == 0x20) { // SAK b2=0 b4=1 b5=1 b1=0 b6=1, SAK=0x38
type |= MTCLASSIC;
} else { // SAK b2=0 b4=1 b5=1 b1=0 b6=0
if (select_status == 4) { // SAK b2=0 b4=1 b5=1 b1=0 b6=0 ATS
if (version_hw_available) { // SAK b2=0 b4=1 b5=1 b1=0 b6=0 ATS GetVersion
type |= MTPLUS;
} else { // SAK b2=0 b4=1 b5=1 b1=0 b6=0 ATS No_GetVersion
if (ats_hist_len > 0) {
if ((ats_hist_len == 9) && (memcmp(ats_hist, "\xC1\x05\x2F\x2F", 4) == 0)) {
type |= MTPLUS;
} else {
type |= MTCLASSIC;
}
}
}
} else { // SAK b2=0 b4=1 b5=1 b1=0 b6=0 no_ATS, SAK=0x18
type |= MTCLASSIC;
}
}
}
type |= (MTDESFIRE | MT424);
}
} else if ((sak & 0x04) == 0x04) {
type |= MTDESFIRE;
} else {
type |= MTULTRALIGHT;
}
} else if ((sak & 0x0A) == 0x0A) {
if ((atqa & 0x0003) == 0x0003) {
type |= MTFUDAN;
} else if ((atqa & 0x0005) == 0x0005) {
} else { // SAK b2=0 b4=1 b5=0
if ((sak & 0x01) == 0x01) { // SAK b2=0 b4=1 b5=0 b1=1, SAK=0x09
type |= MTMINI;
} else { // SAK b2=0 b4=1 b5=0 b1=0
if ((sak & 0x20) == 0x20) { // SAK b2=0 b4=1 b5=0 b1=0 b6=1, SAK=0x28
type |= MTCLASSIC;
} else { // SAK b2=0 b4=1 b5=0 b1=0 b6=0
if (select_status == 4) { // SAK b2=0 b4=1 b5=0 b1=0 b6=0 ATS
if (version_hw_available) { // SAK b2=0 b4=1 b5=0 b1=0 b6=0 ATS GetVersion
type |= MTPLUS;
} else { // SAK b2=0 b4=1 b5=0 b1=0 b6=0 ATS No_GetVersion
if (ats_hist_len > 0) {
if ((ats_hist_len == 9) && (memcmp(ats_hist, "\xC1\x05\x2F\x2F", 4) == 0)) {
type |= MTPLUS;
} else if ((ats_hist_len == 9) && (memcmp(ats_hist, "\xC1\x05\x21\x30", 4) == 0)) {
type |= MTPLUS;
} else {
type |= MTCLASSIC;
}
}
}
} else { // SAK b2=0 b4=1 b5=0 b1=0 b6=0 no_ATS, SAK=0x08
type |= MTCLASSIC;
}
}
}
}
} else { // SAK b2=0 b4=0
if ((sak & 0x10) == 0x10) { // SAK b2=0 b4=0 b5=1
if ((sak & 0x01) == 0x01) { // SAK b2=0 b4=0 b5=1 b1=1, SAK=0x11
type |= MTPLUS;
} else { // SAK b2=0 b4=0 b5=1 b1=0, SAK=0x10
type |= MTPLUS;
}
} else { // SAK b2=0 b4=0 b5=0
if ((sak & 0x01) == 0x01) { // SAK b2=0 b4=0 b5=0 b1=1
type |= MTCLASSIC;
} else { // SAK b2=0 b4=0 b5=0 b1=0
if ((sak & 0x20) == 0x20) { // SAK b2=0 b4=0 b5=0 b1=0 b6=1, SAK=0x20
if (select_status == 1) { // SAK b2=0 b4=0 b5=0 b1=0 b6=1 ATS
if (version_hw_available) { // SAK b2=0 b4=0 b5=0 b1=0 b6=1 ATS GetVersion
if ((version_hw->product_type & 0x7F) == 0x02) {
type |= MTPLUS;
} else if (((version_hw->product_type & 0x7F) == 0x01) ||
(version_hw->product_type == 0x08) ||
(version_hw->product_type == 0x91)) {
type |= MTDESFIRE;
} else if (version_hw->product_type == 0x04) {
type |= (MTDESFIRE | MT424);
}
} else { // SAK b2=0 b4=0 b5=0 b1=0 b6=1 ATS No GetVersion
if (ats_hist_len > 0) {
if ((ats_hist_len == 9) && (memcmp(ats_hist, "\xC1\x05\x2F\x2F", 4) == 0)) {
type |= MTPLUS;
} else {
if ((atqa == 0x0001) || (atqa == 0x0004)) {
type |= HID_SEOS;
}
if (atqa == 0x0004) {
type |= MTEMV;
}
}
}
}
}
} else { // SAK b2=0 b4=0 b5=0 b1=0 b6=0, SAK=0x00
if (version_hw_available == false) {
// SAK b2=0 b4=0 b5=0 b1=0 b6=0 No_GetVersion
int status = mfuc_test_authentication_support();
if (status == PM3_SUCCESS) {
type |= MTULTRALIGHT_C;
}
}
type |= MTULTRALIGHT;
}
}
}
}
} else { // SAK b2=1
if (sak == 0x0A) {
if (atqa == 0x0003) {
// Uses Shanghai algo
type |= MTFUDAN;
} else if (atqa == 0x0005) {
type |= MTFUDAN;
}
} else if (sak == 0x53) {
type |= MTFUDAN;
}
} else if ((sak & 0x53) == 0x53) {
type |= MTFUDAN;
}
return type;
}
typedef struct {
uint8_t vendor_id;
uint8_t product_type;
uint8_t product_subtype;
uint8_t major_product_version;
uint8_t minor_product_version;
uint8_t storage_size;
uint8_t protocol_type;
} version_hw_t;
// Based on NXP AN10833 Rev 3.8 and NXP AN10834 Rev 4.2
static int detect_nxp_card_print(uint8_t sak, uint16_t atqa, uint64_t select_status, uint8_t ats_hist_len, uint8_t *ats_hist, bool version_hw_available, version_hw_t *version_hw) {
static int detect_nxp_card_print(uint8_t sak, uint16_t atqa, uint64_t select_status,
uint8_t ats_hist_len, uint8_t *ats_hist,
bool version_hw_available, version_hw_t *version_hw) {
int type = MTNONE;
const char *product_type_str = "";
const char *major_product_version_str = "";
@ -2237,6 +2422,8 @@ static int detect_nxp_card_print(uint8_t sak, uint16_t atqa, uint64_t select_sta
// TODO: read page 2/3, then ??
printTag("MIFARE Ultralight C");
printTag("MIFARE Hospitality");
type |= MTULTRALIGHT_C;
} else {
printTag("MIFARE Ultralight");
}
@ -2450,36 +2637,12 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) {
ats_hist_pos += (card.ats[1] & 0x40) == 0x40;
}
// field on, card selected if select_status is 1 or 4, not selected if 2
bool version_hw_available = false;
version_hw_t version_hw = {0};
// if 4b UID or NXP, try to get version
if ((card.uidlen == 4) || ((card.uidlen == 7) && (card.uid[0] == 0x04))) {
// GetVersion
if ((select_status == 1) || (select_status == 4)) { // L4
uint8_t response[PM3_CMD_DATA_SIZE] = {0};
int resp_len = 0;
uint8_t getVersion[5] = {0x90, 0x60, 0x00, 0x00, 0x00};
int res = ExchangeAPDU14a(getVersion, sizeof(getVersion), false, false, response, sizeof(response), &resp_len);
if (res == PM3_ETIMEOUT) {
PrintAndLogEx(DEBUG, "iso14443a card select timeout");
DropField();
return PM3_ETIMEOUT;
}
if (resp_len == 9) {
memcpy(&version_hw, response, sizeof(version_hw));
version_hw_available = true;
}
} else { // select_status = 2, L3
uint8_t version[8] = {0};
uint8_t uid[7] = {0};
if (mfu_get_version_uid(version, uid) == PM3_SUCCESS) {
memcpy(&version_hw, version + 1, sizeof(version_hw));
version_hw_available = true;
}
}
}
DropField();
int res = hf14a_getversion_data(&card, select_status, &version_hw);
bool version_hw_available = (res == PM3_SUCCESS);
PrintAndLogEx(INFO, "---------- " _CYAN_("ISO14443-A Information") " ----------");
PrintAndLogEx(SUCCESS, " UID: " _GREEN_("%s") " %s", sprint_hex(card.uid, card.uidlen), get_uid_type(&card));
PrintAndLogEx(SUCCESS, "ATQA: " _GREEN_("%02X %02X"), card.atqa[1], card.atqa[0]);
@ -2501,9 +2664,13 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) {
bool isSEOS = false;
int nxptype = MTNONE;
PrintAndLogEx(SUCCESS, "Possible types:");
if (card.uidlen <= 4) {
nxptype = detect_nxp_card_print(card.sak, ((card.atqa[1] << 8) + card.atqa[0]), select_status, card.ats_len - ats_hist_pos, card.ats + ats_hist_pos, version_hw_available, &version_hw);
PrintAndLogEx(SUCCESS, "Possible types:");
nxptype = detect_nxp_card_print(card.sak, ((card.atqa[1] << 8) + card.atqa[0]),
select_status, card.ats_len - ats_hist_pos, card.ats + ats_hist_pos,
version_hw_available, &version_hw
);
isMifareMini = ((nxptype & MTMINI) == MTMINI);
isMifareClassic = ((nxptype & MTCLASSIC) == MTCLASSIC);
@ -2522,7 +2689,8 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) {
} else {
// Double & triple sized UID, can be mapped to a manufacturer.
PrintAndLogEx(SUCCESS, "MANUFACTURER: " _YELLOW_("%s"), getTagInfo(card.uid[0]));
PrintAndLogEx(SUCCESS, " " _YELLOW_("%s"), getTagInfo(card.uid[0]));
PrintAndLogEx(SUCCESS, "Possible types:");
switch (card.uid[0]) {
case 0x02: { // ST
@ -2531,7 +2699,10 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) {
break;
}
case 0x04: { // NXP
nxptype = detect_nxp_card_print(card.sak, ((card.atqa[1] << 8) + card.atqa[0]), select_status, card.ats_len - ats_hist_pos, card.ats + ats_hist_pos, version_hw_available, &version_hw);
nxptype = detect_nxp_card_print(card.sak, ((card.atqa[1] << 8) + card.atqa[0]),
select_status, card.ats_len - ats_hist_pos, card.ats + ats_hist_pos,
version_hw_available, &version_hw
);
isMifareMini = ((nxptype & MTMINI) == MTMINI);
isMifareClassic = ((nxptype & MTCLASSIC) == MTCLASSIC);
@ -2595,6 +2766,9 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) {
case 0x00: {
isMifareClassic = false;
// ******** is card of the MFU type (UL/ULC/NTAG/ etc etc)
DropField();
uint64_t tagT = GetHF14AMfU_Type();
if (tagT != MFU_TT_UL_ERROR) {
ul_print_type(tagT, 0);
@ -2604,6 +2778,15 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) {
printTag("Possible AZTEK (iso14443a compliant)");
}
// reconnect for further tests
clearCommandBuffer();
SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_NO_DISCONNECT, 0, 0, NULL, 0);
if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) {
PrintAndLogEx(WARNING, "timeout while waiting for reply");
DropField();
return PM3_ETIMEOUT;
}
memcpy(&card, (iso14a_card_select_t *)resp.data.asBytes, sizeof(iso14a_card_select_t));
select_status = resp.oldarg[0]; // 0: couldn't read, 1: OK, with ATS, 2: OK, no ATS
@ -2656,7 +2839,7 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) {
if (card.ats_len >= 3) { // a valid ATS consists of at least the length byte (TL) and 2 CRC bytes
PrintAndLogEx(INFO, "-------------------------- " _CYAN_("ATS") " --------------------------");
PrintAndLogEx(INFO, "-------------------------- " _CYAN_("ATS") " ----------------------------------");
bool ta1 = 0, tb1 = 0, tc1 = 0;
if (select_status == 2) {
@ -2751,7 +2934,7 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) {
if ((card.ats[0] > pos) && (card.ats_len >= card.ats[0] + 2)) {
uint8_t calen = card.ats[0] - pos;
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, "-------------------- " _CYAN_("Historical bytes") " --------------------");
PrintAndLogEx(INFO, "-------------------- " _CYAN_("Historical bytes") " ----------------------------");
if (card.ats[pos] == 0xC1) {
PrintAndLogEx(INFO, " %s", sprint_hex(card.ats + pos, calen));
@ -2856,7 +3039,7 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) {
uint16_t sw = 0;
uint8_t result[1024] = {0};
size_t resultlen = 0;
int res = Iso7816Select(CC_CONTACTLESS, ActivateField, true, vaid, vaidlen, result, sizeof(result), &resultlen, &sw);
res = Iso7816Select(CC_CONTACTLESS, ActivateField, true, vaid, vaidlen, result, sizeof(result), &resultlen, &sw);
ActivateField = false;
if (res)
continue;
@ -2950,7 +3133,7 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) {
}
if (isMifareClassic || isMifareMini) {
int res = detect_classic_static_nonce();
res = detect_classic_static_nonce();
if (res == NONCE_STATIC) {
PrintAndLogEx(SUCCESS, "Static nonce......... " _YELLOW_("yes"));
}

View file

@ -36,6 +36,16 @@ typedef struct {
const char *hint;
} hintAIDList_t;
typedef struct {
uint8_t vendor_id;
uint8_t product_type;
uint8_t product_subtype;
uint8_t major_product_version;
uint8_t minor_product_version;
uint8_t storage_size;
uint8_t protocol_type;
} version_hw_t;
typedef enum {
MTNONE = 0,
MTCLASSIC = 1,
@ -49,6 +59,9 @@ typedef enum {
MTFUDAN = 256,
MTISO18092 = 512,
MT424 = 1024,
MTULTRALIGHT_C = 2048,
MTDUOX = 4096,
MTNTAG = 8192,
} nxp_mifare_type_t;
int CmdHF14A(const char *Cmd);
@ -59,7 +72,10 @@ int CmdHF14ANdefRead(const char *Cmd); // used by cmdnfc.c
int CmdHF14ANdefFormat(const char *Cmd); // used by cmdnfc.c
int CmdHF14ANdefWrite(const char *Cmd); // used by cmdnfc.c
int detect_nxp_card(uint8_t sak, uint16_t atqa, uint64_t select_status);
int detect_nxp_card(uint8_t sak, uint16_t atqa, uint64_t select_status,
uint8_t ats_hist_len, uint8_t *ats_hist,
bool version_hw_available, version_hw_t *version_hw);
int hf14a_getconfig(hf14a_config_t *config);
int hf14a_setconfig(hf14a_config_t *config, bool verbose);
@ -75,4 +91,6 @@ int SelectCard14443A_4_WithParameters(bool disconnect, bool verbose, iso14a_card
bool Get_apdu_in_framing(void);
void Set_apdu_in_framing(bool v);
int hf14a_getversion_data(iso14a_card_select_t *card, uint64_t select_status, version_hw_t *hw);
#endif

View file

@ -372,7 +372,7 @@ int mfc_ev1_print_signature(uint8_t *uid, uint8_t uidlen, uint8_t *signature, in
static int mf_read_uid(uint8_t *uid, int *uidlen, int *nxptype) {
clearCommandBuffer();
SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT, 0, 0, NULL, 0);
SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_NO_DISCONNECT, 0, 0, NULL, 0);
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) {
PrintAndLogEx(DEBUG, "iso14443a card select failed");
@ -383,13 +383,55 @@ static int mf_read_uid(uint8_t *uid, int *uidlen, int *nxptype) {
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];
// try to request ATS even if tag claims not to support it. If yes => 4
if (select_status == 2) {
uint8_t rats[] = { 0xE0, 0x80 }; // FSDI=8 (FSD=256), CID=0
clearCommandBuffer();
SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_RAW | ISO14A_APPEND_CRC | ISO14A_NO_DISCONNECT, 2, 0, rats, sizeof(rats));
if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) {
PrintAndLogEx(WARNING, "timeout while waiting for reply");
return PM3_ETIMEOUT;
}
memcpy(card.ats, resp.data.asBytes, resp.oldarg[0]);
card.ats_len = resp.oldarg[0]; // note: ats_len includes CRC Bytes
if (card.ats_len > 3) {
select_status = 4;
}
}
uint8_t ats_hist_pos = 0;
if ((card.ats_len > 3) && (card.ats[0] > 1)) {
ats_hist_pos = 2;
ats_hist_pos += (card.ats[1] & 0x10) == 0x10;
ats_hist_pos += (card.ats[1] & 0x20) == 0x20;
ats_hist_pos += (card.ats[1] & 0x40) == 0x40;
}
version_hw_t version_hw = {0};
// if 4b UID or NXP, try to get version
int res = hf14a_getversion_data(&card, select_status, &version_hw);
DropField();
bool version_hw_available = (res == PM3_SUCCESS);
if (nxptype) {
uint64_t select_status = resp.oldarg[0];
*nxptype = detect_nxp_card(card.sak, ((card.atqa[1] << 8) + card.atqa[0]), select_status);
*nxptype = detect_nxp_card(card.sak
, ((card.atqa[1] << 8) + card.atqa[0])
, select_status
, card.ats_len - ats_hist_pos
, card.ats + ats_hist_pos
, version_hw_available
, &version_hw
);
}
memcpy(uid, card.uid, card.uidlen * sizeof(uint8_t));
*uidlen = card.uidlen;
return PM3_SUCCESS;
}
@ -636,7 +678,7 @@ static void mf_print_block(uint16_t maxblocks, uint8_t blockno, uint8_t *d, bool
}
}
static void mf_print_blocks(uint16_t n, uint8_t *d, bool verbose) {
void mf_print_blocks(uint16_t n, uint8_t *d, bool verbose) {
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, "-----+-----+-------------------------------------------------+-----------------");
PrintAndLogEx(INFO, " sec | blk | data | ascii");
@ -660,7 +702,7 @@ static void mf_print_blocks(uint16_t n, uint8_t *d, bool verbose) {
}
// assumes n is in number of blocks 0..255
static int mf_print_keys(uint16_t n, uint8_t *d) {
int mf_print_keys(uint16_t n, uint8_t *d) {
uint8_t sectors = 0;
switch (n) {
case MIFARE_MINI_MAXBLOCK:
@ -2967,7 +3009,9 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) {
// Settings
int prng_type = PM3_EUNDEF;
int isOK = 0;
// ------------------------------
PrintAndLogEx(NORMAL, "");
uint64_t tagT = GetHF14AMfU_Type();
if (tagT != MFU_TT_UL_ERROR) {
@ -2978,10 +3022,11 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) {
// Select card to get UID/UIDLEN/ATQA/SAK information
clearCommandBuffer();
SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT, 0, 0, NULL, 0);
SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_NO_DISCONNECT, 0, 0, NULL, 0);
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) {
PrintAndLogEx(DEBUG, "iso14443a card select timeout");
DropField();
return PM3_ETIMEOUT;
}
@ -2997,6 +3042,64 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) {
iso14a_card_select_t card;
memcpy(&card, (iso14a_card_select_t *)resp.data.asBytes, sizeof(iso14a_card_select_t));
// try to request ATS even if tag claims not to support it. If yes => 4
if (select_status == 2) {
uint8_t rats[] = { 0xE0, 0x80 }; // FSDI=8 (FSD=256), CID=0
clearCommandBuffer();
SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_RAW | ISO14A_APPEND_CRC | ISO14A_NO_DISCONNECT, 2, 0, rats, sizeof(rats));
if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) {
PrintAndLogEx(WARNING, "timeout while waiting for reply");
return PM3_ETIMEOUT;
}
memcpy(card.ats, resp.data.asBytes, resp.oldarg[0]);
card.ats_len = resp.oldarg[0]; // note: ats_len includes CRC Bytes
if (card.ats_len > 3) {
select_status = 4;
}
}
uint8_t ats_hist_pos = 0;
if ((card.ats_len > 3) && (card.ats[0] > 1)) {
ats_hist_pos = 2;
ats_hist_pos += (card.ats[1] & 0x10) == 0x10;
ats_hist_pos += (card.ats[1] & 0x20) == 0x20;
ats_hist_pos += (card.ats[1] & 0x40) == 0x40;
}
version_hw_t version_hw = {0};
// if 4b UID or NXP, try to get version
int res = hf14a_getversion_data(&card, select_status, &version_hw);
DropField();
bool version_hw_available = (res == PM3_SUCCESS);
int nxptype = detect_nxp_card(card.sak
, ((card.atqa[1] << 8) + card.atqa[0])
, select_status
, card.ats_len - ats_hist_pos
, card.ats + ats_hist_pos
, version_hw_available
, &version_hw
);
if ((nxptype & MTDESFIRE) == MTDESFIRE) {
PrintAndLogEx(WARNING, "MIFARE DESFire card detected. Quitting...");
return PM3_ESOFT;
}
bool isMifareMini = ((nxptype & MTMINI) == MTMINI);
bool isMifarePlus = ((nxptype & MTPLUS) == MTPLUS);
if (isMifarePlus) {
PrintAndLogEx(INFO, "MIFARE Plus card detected. Using limited set of attacks");
}
if (isMifareMini && sector_cnt != MIFARE_MINI_MAXSECTOR) {
PrintAndLogEx(WARNING, "MIFARE Mini S20 card detected. Changing sector count to %u", MIFARE_MINI_MAXSECTOR);
sector_cnt = MIFARE_MINI_MAXSECTOR;
}
bool known_key = (in_keys_len > 5);
uint8_t key[MIFARE_KEY_SIZE] = {0};
if (known_key) {
@ -3134,7 +3237,7 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) {
return ret;
}
int32_t res = PM3_SUCCESS;
res = PM3_SUCCESS;
// Use the dictionary to find sector keys on the card
if (verbose) {
@ -3465,6 +3568,20 @@ tryNested:
} else {
tryHardnested: // If the nested attack fails then we try the hardnested attack
// skip this
if (isMifarePlus) {
// Show the results to the user
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(SUCCESS, _GREEN_("found keys:"));
printKeyTable(sector_cnt, e_sector);
PrintAndLogEx(NORMAL, "");
free(e_sector);
free(fptr);
return PM3_ESOFT;
}
if (verbose) {
PrintAndLogEx(INFO, "======================= " _YELLOW_("START HARDNESTED ATTACK") " =======================");
PrintAndLogEx(INFO, "sector no %3d, target key type %c, Slow %s",
@ -9578,7 +9695,9 @@ static int CmdHF14AMfValue(const char *Cmd) {
};
CLIExecWithReturn(ctx, Cmd, argtable, false);
uint8_t blockno = (uint8_t)arg_get_int_def(ctx, 13, 1);
int keylen = 0;
uint8_t key[6] = {0};
CLIGetHexWithReturn(ctx, 1, key, &keylen);
uint8_t keytype = MF_KEY_A;
if (arg_get_lit(ctx, 2) && arg_get_lit(ctx, 3)) {
@ -9589,22 +9708,6 @@ static int CmdHF14AMfValue(const char *Cmd) {
keytype = MF_KEY_B;
}
uint8_t transferkeytype = MF_KEY_A;
if (arg_get_lit(ctx, 9) && arg_get_lit(ctx, 10)) {
CLIParserFree(ctx);
PrintAndLogEx(WARNING, "Choose one single transfer key type");
return PM3_EINVARG;
} else if (arg_get_lit(ctx, 10)) {
transferkeytype = MF_KEY_B;
}
int keylen = 0;
uint8_t key[6] = {0};
CLIGetHexWithReturn(ctx, 1, key, &keylen);
int transferkeylen = 0;
uint8_t transferkey[6] = {0};
CLIGetHexWithReturn(ctx, 8, transferkey, &transferkeylen);
/*
Value /Value Value BLK /BLK BLK /BLK
@ -9619,11 +9722,29 @@ static int CmdHF14AMfValue(const char *Cmd) {
int64_t decval = (int64_t)arg_get_u64_def(ctx, 5, -1); // Dec by -1 is invalid, so not set.
int64_t setval = (int64_t)arg_get_u64_def(ctx, 6, 0x7FFFFFFFFFFFFFFF); // out of bounds (for int32) so not set
int64_t trnval = (int64_t)arg_get_u64_def(ctx, 7, -1); // block to transfer to
int transferkeylen = 0;
uint8_t transferkey[6] = {0};
CLIGetHexWithReturn(ctx, 8, transferkey, &transferkeylen);
uint8_t transferkeytype = MF_KEY_A;
if (arg_get_lit(ctx, 9) && arg_get_lit(ctx, 10)) {
CLIParserFree(ctx);
PrintAndLogEx(WARNING, "Choose one single transfer key type");
return PM3_EINVARG;
} else if (arg_get_lit(ctx, 10)) {
transferkeytype = MF_KEY_B;
}
bool getval = arg_get_lit(ctx, 11);
bool resval = arg_get_lit(ctx, 12);
uint8_t blockno = (uint8_t)arg_get_int_def(ctx, 13, 1);
int dlen = 0;
uint8_t data[16] = {0};
CLIGetHexWithReturn(ctx, 14, data, &dlen);
CLIParserFree(ctx);
// sanity checks
@ -10012,10 +10133,11 @@ static int CmdHF14AMfInfo(const char *Cmd) {
}
clearCommandBuffer();
SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT, 0, 0, NULL, 0);
SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_NO_DISCONNECT, 0, 0, NULL, 0);
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) {
PrintAndLogEx(DEBUG, "iso14443a card select timeout");
DropField();
return PM3_ETIMEOUT;
}
@ -10030,6 +10152,23 @@ static int CmdHF14AMfInfo(const char *Cmd) {
*/
uint64_t select_status = resp.oldarg[0];
// try to request ATS even if tag claims not to support it. If yes => 4
if (select_status == 2) {
uint8_t rats[] = { 0xE0, 0x80 }; // FSDI=8 (FSD=256), CID=0
clearCommandBuffer();
SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_RAW | ISO14A_APPEND_CRC | ISO14A_NO_DISCONNECT, 2, 0, rats, sizeof(rats));
if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) {
PrintAndLogEx(WARNING, "timeout while waiting for reply");
return PM3_ETIMEOUT;
}
memcpy(card.ats, resp.data.asBytes, resp.oldarg[0]);
card.ats_len = resp.oldarg[0]; // note: ats_len includes CRC Bytes
if (card.ats_len > 3) {
select_status = 4;
}
}
if (select_status == 0) {
PrintAndLogEx(DEBUG, "iso14443a card select failed");
return select_status;
@ -10050,19 +10189,62 @@ static int CmdHF14AMfInfo(const char *Cmd) {
PrintAndLogEx(SUCCESS, "ATQA: " _GREEN_("%02X %02X"), card.atqa[1], card.atqa[0]);
PrintAndLogEx(SUCCESS, " SAK: " _GREEN_("%02X [%" PRIu64 "]"), card.sak, resp.oldarg[0]);
if (setDeviceDebugLevel(verbose ? MAX(dbg_curr, DBG_INFO) : DBG_NONE, false) != PM3_SUCCESS) {
return PM3_EFAILED;
uint8_t ats_hist_pos = 0;
if ((card.ats_len > 3) && (card.ats[0] > 1)) {
ats_hist_pos = 2;
ats_hist_pos += (card.ats[1] & 0x10) == 0x10;
ats_hist_pos += (card.ats[1] & 0x20) == 0x20;
ats_hist_pos += (card.ats[1] & 0x40) == 0x40;
}
uint64_t tagtype = GetHF14AMfU_Type();
if (tagtype != MFU_TT_UL_ERROR) {
PrintAndLogEx(INFO, "This is not MIFARE Classic based card");
version_hw_t version_hw = {0};
// if 4b UID or NXP, try to get version
int res = hf14a_getversion_data(&card, select_status, &version_hw);
DropField();
bool version_hw_available = (res == PM3_SUCCESS);
int card_type = detect_nxp_card(card.sak
, ((card.atqa[1] << 8) + card.atqa[0])
, select_status
, card.ats_len - ats_hist_pos
, card.ats + ats_hist_pos
, version_hw_available
, &version_hw
);
if ((card_type & MTDESFIRE) == MTDESFIRE) {
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, "MIFARE DESFire detected");
PrintAndLogEx(HINT, "Hint: try `" _YELLOW_("hf mfdes info") "`");
goto out;
}
if ((card_type & MTULTRALIGHT) == MTULTRALIGHT) {
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, "MIFARE Ultralight / NTAG detected");
PrintAndLogEx(HINT, "Hint: try `" _YELLOW_("hf mfu info") "`");
goto out;
}
if ((card_type & MTPLUS) == MTPLUS) {
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, "MIFARE Plus detected");
PrintAndLogEx(HINT, "Hint: try `" _YELLOW_("hf mfp info") "`");
}
if ((card_type & MTEMV) == MTEMV) {
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, "EMV detected");
PrintAndLogEx(HINT, "Hint: try `" _YELLOW_("emv info") "`");
}
if (setDeviceDebugLevel(verbose ? MAX(dbg_curr, DBG_INFO) : DBG_NONE, false) != PM3_SUCCESS) {
return PM3_EFAILED;
}
uint8_t signature[32] = {0};
int res = read_mfc_ev1_signature(signature);
res = read_mfc_ev1_signature(signature);
if (res == PM3_SUCCESS) {
mfc_ev1_print_signature(card.uid, card.uidlen, signature, sizeof(signature));
}

View file

@ -35,9 +35,13 @@ void printKeyTable(size_t sectorscnt, sector_t *e_sector);
void printKeyTableEx(size_t sectorscnt, sector_t *e_sector, uint8_t start_sector);
// void printKeyTableEx(size_t sectorscnt, sector_t *e_sector, uint8_t start_sector, bool singel_sector);
bool mfc_value(const uint8_t *d, int32_t *val);
void mf_print_sector_hdr(uint8_t sector);
void mf_print_block_one(uint8_t blockno, uint8_t *d, bool verbose);
int mf_print_keys(uint16_t n, uint8_t *d);
void mf_print_blocks(uint16_t n, uint8_t *d, bool verbose);
int mfc_ev1_print_signature(uint8_t *uid, uint8_t uidlen, uint8_t *signature, int signature_len);
#endif

View file

@ -89,15 +89,6 @@ typedef struct mfdes_data {
uint8_t *data;
} PACKED mfdes_data_t;
typedef struct mfdes_info_res {
uint8_t isOK;
uint8_t uid[7];
uint8_t uidlen;
uint8_t versionHW[7];
uint8_t versionSW[7];
uint8_t details[14];
} PACKED mfdes_info_res_t;
typedef struct mfdes_value {
uint8_t fileno; //01
uint8_t value[16];
@ -136,21 +127,6 @@ typedef enum {
MFDES_VALUE_FILE
} MFDES_FILE_TYPE_T;
typedef enum {
DESFIRE_UNKNOWN = 0,
DESFIRE_MF3ICD40,
DESFIRE_EV1,
DESFIRE_EV2,
DESFIRE_EV2_XL,
DESFIRE_EV3,
DESFIRE_LIGHT,
PLUS_EV1,
PLUS_EV2,
NTAG413DNA,
NTAG424,
DUOX,
} nxp_cardtype_t;
typedef enum {
DESFIRE_UNKNOWN_PROD = 0,
DESFIRE_PHYSICAL,
@ -332,7 +308,7 @@ static const char *getAidCommentStr(uint32_t aid) {
return "";
}
static nxp_cardtype_t getCardType(uint8_t type, uint8_t major, uint8_t minor) {
nxp_cardtype_t getCardType(uint8_t type, uint8_t major, uint8_t minor) {
// DESFire MF3ICD40
if (type == 0x01 && major == 0x00 && minor == 0x02)
@ -385,7 +361,7 @@ static nxp_cardtype_t getCardType(uint8_t type, uint8_t major, uint8_t minor) {
if (type == 0x04 && major == 0x30 && minor == 0x00)
return NTAG424;
return DESFIRE_UNKNOWN;
return NXP_UNKNOWN;
}
// ref: https://www.nxp.com/docs/en/application-note/AN12343.pdf p7
@ -423,7 +399,7 @@ static const char *getProductTypeStr(const uint8_t *versionhw) {
return "UNKNOWN PROD";
}
static int mfdes_get_info(mfdes_info_res_t *info) {
int mfdes_get_info(mfdes_info_res_t *info) {
PacketResponseNG resp;
SendCommandNG(CMD_HF_DESFIRE_INFO, NULL, 0);
@ -710,7 +686,7 @@ static int CmdHF14ADesInfo(const char *Cmd) {
return PM3_SUCCESS;
}
if (cardtype == DESFIRE_UNKNOWN) {
if (cardtype == NXP_UNKNOWN) {
PrintAndLogEx(INFO, "HW Version.. %s", sprint_hex_inrow(info.versionHW, sizeof(info.versionHW)));
PrintAndLogEx(INFO, "SW Version.. %s", sprint_hex_inrow(info.versionSW, sizeof(info.versionSW)));
PrintAndLogEx(INFO, "Version data identification failed. Report to Iceman!");

View file

@ -20,6 +20,44 @@
#include "common.h"
// Ev1 card limits
#define MAX_NUM_KEYS 0x0F
#define MAX_APPLICATION_COUNT 28
#define MAX_FILE_COUNT 32
#define MAX_FRAME_SIZE 60
#define FRAME_PAYLOAD_SIZE (MAX_FRAME_SIZE - 5)
// Ev2 card limits
// Ev3 card limits
// Light card limits
// Light Ev1 card limits
#define NOT_YET_AUTHENTICATED 0xFF
typedef enum {
NXP_UNKNOWN = 0,
DESFIRE_MF3ICD40,
DESFIRE_EV1,
DESFIRE_EV2,
DESFIRE_EV2_XL,
DESFIRE_EV3,
DESFIRE_LIGHT,
PLUS_EV1,
PLUS_EV2,
NTAG413DNA,
NTAG424,
DUOX,
} nxp_cardtype_t;
typedef struct {
uint8_t isOK;
uint8_t uid[7];
uint8_t uidlen;
uint8_t versionHW[7];
uint8_t versionSW[7];
uint8_t details[14];
} PACKED mfdes_info_res_t;
int CmdHFMFDes(const char *Cmd);
/*
@ -29,24 +67,7 @@ int getKeySettings(uint8_t *aid);
*/
int desfire_print_signature(uint8_t *uid, uint8_t uidlen, uint8_t *signature, size_t signature_len);
// Ev1 card limits
#define MAX_NUM_KEYS 0x0F
#define MAX_APPLICATION_COUNT 28
#define MAX_FILE_COUNT 32
#define MAX_FRAME_SIZE 60
#define FRAME_PAYLOAD_SIZE (MAX_FRAME_SIZE - 5)
// Ev2 card limits
// Ev3 card limits
// Light card limits
// Light Ev1 card limits
#define NOT_YET_AUTHENTICATED 0xFF
nxp_cardtype_t getCardType(uint8_t type, uint8_t major, uint8_t minor);
int mfdes_get_info(mfdes_info_res_t *info);
#endif

View file

@ -17,6 +17,7 @@
//-----------------------------------------------------------------------------
#include "cmdhfmfp.h"
#include "cmdhfmfdes.h"
#include <string.h>
#include "cmdparser.h" // command_t
#include "commonutil.h" // ARRAYLEN
@ -41,6 +42,7 @@ static const uint8_t mfp_default_key[16] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
static uint16_t mfp_card_adresses[] = {0x9000, 0x9001, 0x9002, 0x9003, 0x9004, 0x9006, 0x9007, 0xA000, 0xA001, 0xA080, 0xA081, 0xC000, 0xC001};
#define MFP_KEY_FILE_SIZE 14 + (2 * 64 * (AES_KEY_LEN + 1))
#define MFP_CHK_KEY_TRIES (2)
static int CmdHelp(const char *Cmd);
@ -141,54 +143,6 @@ static char *getTypeStr(uint8_t type) {
return buf;
}
static nxp_cardtype_t getCardType(uint8_t type, uint8_t major, uint8_t minor) {
// DESFire MF3ICD40
if (type == 0x01 && major == 0x00 && minor == 0x02)
return DESFIRE_MF3ICD40;
// DESFire EV1
if (type == 0x01 && major == 0x01 && minor == 0x00)
return DESFIRE_EV1;
// DESFire EV2
if (type == 0x01 && major == 0x12 && minor == 0x00)
return DESFIRE_EV2;
if (type == 0x01 && major == 0x22 && minor == 0x00)
return DESFIRE_EV2_XL;
// DESFire EV3
if (type == 0x01 && major == 0x33 && minor == 0x00)
return DESFIRE_EV3;
// DESFire Light
if (type == 0x08 && major == 0x30 && minor == 0x00)
return DESFIRE_LIGHT;
// combo card DESFire / EMV
if (type == 0x81 && major == 0x42 && minor == 0x00)
return DESFIRE_EV2;
// Plus EV1
if (type == 0x02 && major == 0x11 && minor == 0x00)
return PLUS_EV1;
// Plus Ev2
if (type == 0x02 && major == 0x22 && minor == 0x00)
return PLUS_EV2;
// NTAG 413 DNA
if (type == 0x04 && major == 0x10 && minor == 0x00)
return NTAG413DNA;
// NTAG 424
if (type == 0x04 && major == 0x30 && minor == 0x00)
return NTAG424;
return MFP_UNKNOWN;
}
// --- GET SIGNATURE
static int plus_print_signature(uint8_t *uid, uint8_t uidlen, uint8_t *signature, int signature_len) {
int index = originality_check_verify(uid, uidlen, signature, signature_len, PK_MFP);
@ -259,6 +213,149 @@ static int get_plus_version(uint8_t *version, int *version_len) {
return retval;
}
static int mfp_read_card_id(iso14a_card_select_t *card, int *nxptype) {
if (card == NULL) {
return PM3_EINVARG;
}
clearCommandBuffer();
SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_NO_DISCONNECT, 0, 0, NULL, 0);
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) {
PrintAndLogEx(DEBUG, "iso14443a card select failed");
DropField();
return PM3_ERFTRANS;
}
memcpy(card, (iso14a_card_select_t *)resp.data.asBytes, sizeof(iso14a_card_select_t));
if (nxptype) {
uint64_t select_status = resp.oldarg[0];
uint8_t ats_hist_pos = 0;
if ((card->ats_len > 3) && (card->ats[0] > 1)) {
ats_hist_pos = 2;
ats_hist_pos += (card->ats[1] & 0x10) == 0x10;
ats_hist_pos += (card->ats[1] & 0x20) == 0x20;
ats_hist_pos += (card->ats[1] & 0x40) == 0x40;
}
version_hw_t version_hw = {0};
// if 4b UID or NXP, try to get version
int res = hf14a_getversion_data(card, select_status, &version_hw);
DropField();
bool version_hw_available = (res == PM3_SUCCESS);
*nxptype = detect_nxp_card(card->sak
, ((card->atqa[1] << 8) + card->atqa[0])
, select_status
, card->ats_len - ats_hist_pos
, card->ats + ats_hist_pos
, version_hw_available
, &version_hw
);
}
return PM3_SUCCESS;
}
static int mfp_load_keygen_keys(uint8_t **pkeyBlock, uint32_t *pkeycnt, uint8_t *uid) {
// Handle dymanica keys
return PM3_SUCCESS;
}
static int mfp_load_keys(uint8_t **pkeyBlock, uint32_t *pkeycnt, uint8_t *userkey, int userkeylen, const char *filename, int fnlen, uint8_t *uid, bool load_default) {
// Handle Keys
*pkeycnt = 0;
*pkeyBlock = NULL;
uint8_t *p;
// Handle KDF uid based keys
if (uid) {
mfp_load_keygen_keys(pkeyBlock, pkeycnt, uid);
}
// Handle user supplied key
// (it considers *pkeycnt and *pkeyBlock as possibly non-null so logic can be easily reordered)
if (userkeylen >= AES_KEY_LEN) {
int numKeys = userkeylen / AES_KEY_LEN;
p = realloc(*pkeyBlock, (*pkeycnt + numKeys) * AES_KEY_LEN);
if (p == NULL) {
PrintAndLogEx(WARNING, "Failed to allocate memory");
free(*pkeyBlock);
return PM3_EMALLOC;
}
*pkeyBlock = p;
memcpy(*pkeyBlock, userkey, numKeys * AES_KEY_LEN);
for (int i = 0; i < numKeys; i++) {
PrintAndLogEx(DEBUG, _YELLOW_("%2d") " - %s", i, sprint_hex_inrow(*pkeyBlock + i * AES_KEY_LEN, AES_KEY_LEN));
}
*pkeycnt += numKeys;
PrintAndLogEx(SUCCESS, "loaded " _GREEN_("%d") " user keys", numKeys);
}
if (load_default) {
// Handle default keys
p = realloc(*pkeyBlock, (*pkeycnt + g_mifare_plus_default_keys_len) * AES_KEY_LEN);
if (p == NULL) {
PrintAndLogEx(WARNING, "Failed to allocate memory");
free(*pkeyBlock);
return PM3_EMALLOC;
}
*pkeyBlock = p;
// Copy default keys to list
int cnt = 0;
for (cnt = 0; cnt < g_mifare_plus_default_keys_len; cnt++) {
int len = hex_to_bytes(g_mifare_plus_default_keys[cnt], (uint8_t *)(*pkeyBlock + (*pkeycnt + cnt) * AES_KEY_LEN), AES_KEY_LEN);
PrintAndLogEx(DEBUG, _YELLOW_("%2d") " - %s", *pkeycnt + cnt, sprint_hex_inrow(*pkeyBlock + (*pkeycnt + cnt) * AES_KEY_LEN, AES_KEY_LEN));
if (len != AES_KEY_LEN) {
break;
}
}
*pkeycnt += cnt;
PrintAndLogEx(SUCCESS, "loaded " _GREEN_("%zu") " hardcoded keys", cnt);
}
// Handle user supplied dictionary file
if (fnlen > 0) {
uint32_t loaded_numKeys = 0;
uint8_t *dict_keys = NULL;
int res = loadFileDICTIONARY_safe(filename, (void **) &dict_keys, AES_KEY_LEN, &loaded_numKeys);
if (res != PM3_SUCCESS || loaded_numKeys == 0 || dict_keys == NULL) {
PrintAndLogEx(FAILED, "An error occurred while loading the dictionary!");
free(dict_keys);
free(*pkeyBlock);
return PM3_EFILE;
} else {
p = realloc(*pkeyBlock, (*pkeycnt + loaded_numKeys) * AES_KEY_LEN);
if (p == NULL) {
PrintAndLogEx(WARNING, "Failed to allocate memory");
free(dict_keys);
free(*pkeyBlock);
return PM3_EMALLOC;
}
*pkeyBlock = p;
memcpy(*pkeyBlock + *pkeycnt * AES_KEY_LEN, dict_keys, loaded_numKeys * AES_KEY_LEN);
*pkeycnt += loaded_numKeys;
free(dict_keys);
}
}
return PM3_SUCCESS;
}
static int CmdHFMFPInfo(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf mfp info",
@ -730,29 +827,47 @@ static int CmdHFMFPAuth(const char *Cmd) {
return MifareAuth4(NULL, keyn, key, true, false, true, verbose, false);
}
static int data_crypt(mf4Session_t *mf4session, uint8_t *dati, uint8_t *dato, bool rev) {
uint8_t kenc[16];
memcpy(kenc, mf4session->Kenc, 16);
int mfp_data_crypt(mf4Session_t *mf4session, uint8_t *dati, uint8_t *dato, bool rev) {
uint8_t kenc[MFBLOCK_SIZE];
memcpy(kenc, mf4session->Kenc, MFBLOCK_SIZE);
uint8_t ti[4];
memcpy(ti, mf4session->TI, 4);
uint8_t ctr[1];
uint8_t IV[16] = {0, 0, 0x00, 0x00, 0x00, 0, 0x00, 0x00, 0x00, 0};
uint8_t IV[MFBLOCK_SIZE] = { 0 };
if (rev) {
ctr[0] = (uint8_t)(mf4session->R_Ctr & 0xff);
for (int i = 0; i < 9; i += 4) {memcpy(&IV[i], ctr, 1);}
ctr[0] = (uint8_t)(mf4session->R_Ctr & 0xFF);
for (int i = 0; i < 9; i += 4) {
memcpy(&IV[i], ctr, 1);
}
memcpy(&IV[12], ti, 4); // For reads TI is LS
} else {
ctr[0] = (uint8_t)(mf4session->W_Ctr & 0xff);
for (int i = 3; i < 16; i += 4) {memcpy(&IV[i], ctr, 1);}
ctr[0] = (uint8_t)(mf4session->W_Ctr & 0xFF);
for (int i = 3; i < MFBLOCK_SIZE; i += 4) {
memcpy(&IV[i], ctr, 1);
}
memcpy(&IV[0], ti, 4); // For writes TI is MS
}
if (rev) {
aes_decode(IV, kenc, dati, dato, 16);
aes_decode(IV, kenc, dati, dato, MFBLOCK_SIZE);
} else {
aes_encode(IV, kenc, dati, dato, 16);
aes_encode(IV, kenc, dati, dato, MFBLOCK_SIZE);
}
return 0;
return PM3_SUCCESS;
}
static int CmdHFMFPRdbl(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf mfp rdbl",
@ -791,7 +906,7 @@ static int CmdHFMFPRdbl(const char *Cmd) {
mfpSetVerboseMode(verbose);
if (!keylen) {
if (keylen == 0) {
memmove(key, mfp_default_key, 16);
keylen = 16;
}
@ -820,8 +935,9 @@ static int CmdHFMFPRdbl(const char *Cmd) {
uint16_t uKeyNum = 0x4000 + sectorNum * 2 + (keyB ? 1 : 0);
keyn[0] = uKeyNum >> 8;
keyn[1] = uKeyNum & 0xff;
if (verbose)
if (verbose) {
PrintAndLogEx(INFO, "--block:%d sector[%u]:%02x key:%04x", blockn, mfNumBlocksPerSector(sectorNum), sectorNum, uKeyNum);
}
mf4Session_t mf4session;
int res = MifareAuth4(&mf4session, keyn, key, true, true, true, verbose, false);
@ -849,7 +965,10 @@ static int CmdHFMFPRdbl(const char *Cmd) {
return PM3_ESOFT;
}
if (!plain) data_crypt(&mf4session, &data[1], &data[1], true);
if (plain == false) {
mfp_data_crypt(&mf4session, &data[1], &data[1], true);
}
uint8_t sector = mfSectorNum(blockn);
mf_print_sector_hdr(sector);
@ -907,7 +1026,7 @@ static int CmdHFMFPRdsc(const char *Cmd) {
mfpSetVerboseMode(verbose);
if (!keylen) {
if (keylen == 0) {
memmove(key, mfp_default_key, 16);
keylen = 16;
}
@ -925,8 +1044,9 @@ static int CmdHFMFPRdsc(const char *Cmd) {
uint16_t uKeyNum = 0x4000 + sectorNum * 2 + (keyB ? 1 : 0);
keyn[0] = uKeyNum >> 8;
keyn[1] = uKeyNum & 0xff;
if (verbose)
if (verbose) {
PrintAndLogEx(INFO, "--sector[%u]:%02x key:%04x", mfNumBlocksPerSector(sectorNum), sectorNum, uKeyNum);
}
mf4Session_t mf4session;
int res = MifareAuth4(&mf4session, keyn, key, true, true, true, verbose, false);
@ -961,7 +1081,11 @@ static int CmdHFMFPRdsc(const char *Cmd) {
DropField();
return PM3_ESOFT;
}
if (!plain) data_crypt(&mf4session, &data[1], &data[1], true);
if (plain == false) {
mfp_data_crypt(&mf4session, &data[1], &data[1], true);
}
mf_print_block_one(blockno, data + 1, verbose);
if (memcmp(&data[1 + 16], mac, 8) && !nomacres) {
@ -1043,8 +1167,9 @@ static int CmdHFMFPWrbl(const char *Cmd) {
uint16_t uKeyNum = 0x4000 + sectorNum * 2 + (keyB ? 1 : 0);
keyn[0] = uKeyNum >> 8;
keyn[1] = uKeyNum & 0xff;
if (verbose)
if (verbose) {
PrintAndLogEx(INFO, "--block:%d sector[%u]:%02x key:%04x", blockNum & 0xff, mfNumBlocksPerSector(sectorNum), sectorNum, uKeyNum);
}
mf4Session_t mf4session;
int res = MifareAuth4(&mf4session, keyn, key, true, true, true, verbose, false);
@ -1052,7 +1177,11 @@ static int CmdHFMFPWrbl(const char *Cmd) {
PrintAndLogEx(ERR, "Authentication error: %d", res);
return res;
}
if (!plain) data_crypt(&mf4session, &datain[0], &datain[0], false);
if (plain == false) {
mfp_data_crypt(&mf4session, &datain[0], &datain[0], false);
}
uint8_t data[250] = {0};
int datalen = 0;
uint8_t mac[8] = {0};
@ -1146,24 +1275,35 @@ static int CmdHFMFPChKey(const char *Cmd) {
PrintAndLogEx(ERR, "<data> must be 16 bytes. Got %d", datainlen);
return PM3_EINVARG;
}
mf4Session_t mf4session;
keyn[0] = ki[0];
if (ki[0] == 0x40) { // Only if we are working with sector keys
if (usekeyb) {
keyn[1] = (ki[1] % 2 == 0) ? ki[1] + 1 : ki[1]; // If we change using key B, check if KI is key A
} else {
keyn[1] = (ki[1] % 2 == 0) ? ki[1] : ki[1] - 1; // If we change using key A, check if KI is key A
}
} else {keyn[1] = ki[1];}
} else {
keyn[1] = ki[1];
}
if (verbose) {
PrintAndLogEx(INFO, "--key index:", sprint_hex(keyn, 2));
}
int res = MifareAuth4(&mf4session, keyn, key, true, true, true, verbose, false);
if (res) {
PrintAndLogEx(ERR, "Authentication error: %d", res);
return res;
}
data_crypt(&mf4session, &datain[0], &datain[0], false);
mfp_data_crypt(&mf4session, &datain[0], &datain[0], false);
uint8_t data[250] = {0};
int datalen = 0;
uint8_t mac[8] = {0};
@ -1191,8 +1331,9 @@ static int CmdHFMFPChKey(const char *Cmd) {
PrintAndLogEx(WARNING, "MAC card: %s", sprint_hex(&data[1], 8));
PrintAndLogEx(WARNING, "MAC reader: %s", sprint_hex(mac, 8));
} else if (!nomacres) {
if (verbose)
if (verbose) {
PrintAndLogEx(INFO, "MAC: %s", sprint_hex(&data[1], 8));
}
}
DropField();
@ -1245,7 +1386,7 @@ static int CmdHFMFPChConf(const char *Cmd) {
mfpSetVerboseMode(verbose);
if (!keylen) {
if (keylen == 0) {
memmove(key, mfp_default_key, 16);
keylen = 16;
}
@ -1264,18 +1405,23 @@ static int CmdHFMFPChConf(const char *Cmd) {
PrintAndLogEx(ERR, "<config number> must be in range [0..3]. Got %d", blockNum);
return PM3_EINVARG;
}
mf4Session_t mf4session;
keyn[0] = 0x90;
keyn[1] = usecck ? 0x01 : 0x00;
if (verbose) {
PrintAndLogEx(INFO, "--key index:", sprint_hex(keyn, 2));
}
int res = MifareAuth4(&mf4session, keyn, key, true, true, true, verbose, false);
if (res) {
PrintAndLogEx(ERR, "Authentication error: %d", res);
return res;
}
data_crypt(&mf4session, &datain[0], &datain[0], false);
mfp_data_crypt(&mf4session, &datain[0], &datain[0], false);
uint8_t data[250] = {0};
int datalen = 0;
uint8_t mac[8] = {0};
@ -1302,9 +1448,10 @@ static int CmdHFMFPChConf(const char *Cmd) {
PrintAndLogEx(WARNING, "WARNING: mac not equal...");
PrintAndLogEx(WARNING, "MAC card: %s", sprint_hex(&data[1], 8));
PrintAndLogEx(WARNING, "MAC reader: %s", sprint_hex(mac, 8));
} else if (!nomacres) {
if (verbose)
} else if (nomacres == false) {
if (verbose) {
PrintAndLogEx(INFO, "MAC: %s", sprint_hex(&data[1], 8));
}
}
DropField();
@ -1312,19 +1459,33 @@ static int CmdHFMFPChConf(const char *Cmd) {
return PM3_SUCCESS;
}
static int plus_key_check(uint8_t startSector, uint8_t endSector, uint8_t startKeyAB, uint8_t endKeyAB,
uint8_t keyList[MAX_AES_KEYS_LIST_LEN][AES_KEY_LEN], size_t keyListLen, uint8_t foundKeys[2][64][AES_KEY_LEN + 1],
bool verbose) {
int res;
bool selectCard = true;
uint8_t keyn[2] = {0};
static int plus_key_check(uint8_t start_sector, uint8_t end_sector, uint8_t startKeyAB, uint8_t endKeyAB,
uint8_t *keys, size_t keycount, uint8_t foundKeys[2][64][AES_KEY_LEN + 1],
bool verbose, bool newline) {
if (newline) {
PrintAndLogEx(INFO, "." NOLF);
}
// sector number from 0
for (uint8_t sector = startSector; sector <= endSector; sector++) {
for (uint8_t sector = start_sector; sector <= end_sector; sector++) {
// 0-keyA 1-keyB
for (uint8_t keyAB = startKeyAB; keyAB <= endKeyAB; keyAB++) {
// skip already found keys
if (foundKeys[keyAB][sector][0]) {
continue;
}
int res;
bool selectCard = true;
// reset current key pointer after each loop
uint8_t *currkey = keys;
// main cycle with key check
for (int i = 0; i < keyListLen; i++) {
for (int i = 0; i < keycount; i++) {
// allow client abort every iteration
if (kbd_enter_pressed()) {
@ -1340,18 +1501,21 @@ static int plus_key_check(uint8_t startSector, uint8_t endSector, uint8_t startK
}
uint16_t uKeyNum = 0x4000 + sector * 2 + keyAB;
keyn[0] = uKeyNum >> 8;
keyn[1] = uKeyNum & 0xff;
uint8_t keyn[2] = { uKeyNum >> 8, uKeyNum & 0xff};
for (int retry = 0; retry < 4; retry++) {
res = MifareAuth4(NULL, keyn, keyList[i], selectCard, true, false, false, true);
if (res == PM3_SUCCESS || res == PM3_EWRONGANSWER)
// authentication loop with retries
for (int retry = 0; retry < MFP_CHK_KEY_TRIES; retry++) {
res = MifareAuth4(NULL, keyn, currkey, selectCard, true, false, false, true);
if (res == PM3_SUCCESS || res == PM3_EWRONGANSWER) {
break;
}
if (verbose)
if (verbose) {
PrintAndLogEx(WARNING, "\nretried[%d]...", retry);
else
} else {
PrintAndLogEx(NORMAL, "R" NOLF);
}
DropField();
selectCard = true;
@ -1360,38 +1524,52 @@ static int plus_key_check(uint8_t startSector, uint8_t endSector, uint8_t startK
// key for [sector,keyAB] found
if (res == PM3_SUCCESS) {
if (verbose)
PrintAndLogEx(INFO, "\nFound key for sector %d key %s [%s]", sector, keyAB == 0 ? "A" : "B", sprint_hex_inrow(keyList[i], 16));
else
PrintAndLogEx(NORMAL, "+" NOLF);
if (verbose) {
PrintAndLogEx(INFO, "Found key for sector " _YELLOW_("%d") " key "_YELLOW_("%s") " [ " _GREEN_("%s") " ]", sector, (keyAB == 0) ? "A" : "B", sprint_hex_inrow(currkey, AES_KEY_LEN));
} else {
PrintAndLogEx(NORMAL, _GREEN_("+") NOLF);
}
foundKeys[keyAB][sector][0] = 0x01;
memcpy(&foundKeys[keyAB][sector][1], keyList[i], AES_KEY_LEN);
memcpy(&foundKeys[keyAB][sector][1], currkey, AES_KEY_LEN);
DropField();
selectCard = true;
msleep(50);
// msleep(50);
// recursive test of a successful key
if (keycount > 1) {
plus_key_check(start_sector, end_sector, startKeyAB, endKeyAB, currkey, 1, foundKeys, verbose, false);
}
// break out from keylist check loop,
break;
}
if (verbose)
PrintAndLogEx(WARNING, "\nsector %02d key %d [%s] res: %d", sector, keyAB, sprint_hex_inrow(keyList[i], 16), res);
if (verbose) {
PrintAndLogEx(WARNING, "\nsector %02d key %d [%s] res: %d", sector, keyAB, sprint_hex_inrow(currkey, AES_KEY_LEN), res);
}
// RES can be:
// PM3_ERFTRANS -7
// PM3_EWRONGANSWER -16
if (res == PM3_ERFTRANS) {
if (verbose)
if (verbose) {
PrintAndLogEx(ERR, "\nExchange error. Aborted.");
else
} else {
PrintAndLogEx(NORMAL, "E" NOLF);
}
DropField();
return PM3_ECARDEXCHANGE;
}
selectCard = false;
// set pointer to next key
currkey += AES_KEY_LEN;
}
}
}
@ -1400,18 +1578,31 @@ static int plus_key_check(uint8_t startSector, uint8_t endSector, uint8_t startK
return PM3_SUCCESS;
}
static void Fill2bPattern(uint8_t keyList[MAX_AES_KEYS_LIST_LEN][AES_KEY_LEN], uint32_t *keyListLen, uint32_t *startPattern) {
static void Fill2bPattern(uint8_t keyList[MAX_AES_KEYS_LIST_LEN][AES_KEY_LEN], uint32_t *n, uint32_t *startPattern) {
uint32_t cnt = 0;
for (uint32_t pt = *startPattern; pt < 0x10000; pt++) {
keyList[*keyListLen][0] = (pt >> 8) & 0xff;
keyList[*keyListLen][1] = pt & 0xff;
memcpy(&keyList[*keyListLen][2], &keyList[*keyListLen][0], 2);
memcpy(&keyList[*keyListLen][4], &keyList[*keyListLen][0], 4);
memcpy(&keyList[*keyListLen][8], &keyList[*keyListLen][0], 8);
(*keyListLen)++;
for (uint8_t i = 0; i < AES_KEY_LEN; i += 2) {
keyList[*n][i] = (pt >> 8) & 0xff;
keyList[*n][i + 1] = pt & 0xff;
}
PrintAndLogEx(DEBUG, _YELLOW_("%4d") " - %s", *n, sprint_hex_inrow(keyList[*n], AES_KEY_LEN));
// increase number of keys
(*n)++;
cnt++;
*startPattern = pt;
if (*keyListLen == MAX_AES_KEYS_LIST_LEN)
if (*n == MAX_AES_KEYS_LIST_LEN) {
break;
}
}
PrintAndLogEx(SUCCESS, "loaded " _GREEN_("%d") " pattern2 keys", cnt);
(*startPattern)++;
}
@ -1422,7 +1613,7 @@ static int CmdHFMFPChk(const char *Cmd) {
"Checks keys on MIFARE Plus card",
"hf mfp chk -k 000102030405060708090a0b0c0d0e0f -> check key on sector 0 as key A and B\n"
"hf mfp chk -s 2 -a -> check default key list on sector 2, only key A\n"
"hf mfp chk -f mfp_default_keys -s0 -e6 -> check keys from dictionary against sectors 0-6\n"
"hf mfp chk -f mfp_default_keys -s 0 -e 6 -> check keys from dictionary against sectors 0-6\n"
"hf mfp chk --pattern1b --dump -> check all 1-byte keys pattern and save found keys to file\n"
"hf mfp chk --pattern2b --startp2b FA00 -> check all 2-byte keys pattern. Start from key FA00FA00...FA00"
);
@ -1439,6 +1630,7 @@ static int CmdHFMFPChk(const char *Cmd) {
arg_lit0(NULL, "pattern2b", "Check all 2-byte combinations of key (0000...0000, 0001...0001, 0002...0002, ...)"),
arg_str0(NULL, "startp2b", "<pattern>", "Start key (2-byte HEX) for 2-byte search (use with `--pattern2b`)"),
arg_lit0(NULL, "dump", "Dump found keys to JSON file"),
arg_lit0(NULL, "no-default", "Skip check default keys"),
arg_lit0("v", "verbose", "Verbose output"),
arg_param_end
};
@ -1446,70 +1638,71 @@ static int CmdHFMFPChk(const char *Cmd) {
bool keyA = arg_get_lit(ctx, 1);
bool keyB = arg_get_lit(ctx, 2);
uint8_t startSector = arg_get_int_def(ctx, 3, 0);
uint8_t endSector = arg_get_int_def(ctx, 4, 0);
uint8_t keyList[MAX_AES_KEYS_LIST_LEN][AES_KEY_LEN] = {{0}};
uint32_t keyListLen = 0;
uint8_t keyList[MAX_AES_KEYS_LIST_LEN][AES_KEY_LEN] = {{0}};
uint8_t foundKeys[2][64][AES_KEY_LEN + 1] = {{{0}}};
uint8_t vkey[16] = {0};
int vkeylen = 0;
uint8_t vkey[AES_KEY_LEN] = {0};
CLIGetHexWithReturn(ctx, 5, vkey, &vkeylen);
if (vkeylen > 0) {
if (vkeylen == 16) {
memcpy(&keyList[keyListLen], vkey, 16);
keyListLen++;
} else {
PrintAndLogEx(ERR, "Specified key must have 16 bytes. Got %d", vkeylen);
CLIParserFree(ctx);
return PM3_EINVARG;
}
}
uint8_t dict_filename[FILE_PATH_SIZE + 2] = {0};
int dict_filenamelen = 0;
if (CLIParamStrToBuf(arg_get_str(ctx, 6), dict_filename, FILE_PATH_SIZE, &dict_filenamelen)) {
PrintAndLogEx(FAILED, "File name too long or invalid.");
CLIParserFree(ctx);
return PM3_EINVARG;
}
int fnlen = 0;
char filename[FILE_PATH_SIZE] = {0};
CLIParamStrToBuf(arg_get_str(ctx, 6), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen);
bool pattern1b = arg_get_lit(ctx, 7);
bool pattern2b = arg_get_lit(ctx, 8);
if (pattern1b && pattern2b) {
PrintAndLogEx(ERR, "Pattern search mode must be 2-byte or 1-byte only.");
CLIParserFree(ctx);
return PM3_EINVARG;
}
if (dict_filenamelen && (pattern1b || pattern2b)) {
PrintAndLogEx(ERR, "Pattern search mode and dictionary mode can't be used in one command.");
CLIParserFree(ctx);
return PM3_EINVARG;
}
uint32_t startPattern = 0x0000;
uint8_t vpattern[2];
int vpatternlen = 0;
uint8_t vpattern[2];
CLIGetHexWithReturn(ctx, 9, vpattern, &vpatternlen);
if (vpatternlen > 0) {
if (vpatternlen <= 2) {
startPattern = (vpattern[0] << 8) + vpattern[1];
} else {
PrintAndLogEx(ERR, "Pattern must be 2-bytes. Got %d", vpatternlen);
CLIParserFree(ctx);
return PM3_EINVARG;
}
if (!pattern2b)
PrintAndLogEx(WARNING, "Pattern entered, but search mode not is 2-byte search.");
}
bool create_dumpfile = arg_get_lit(ctx, 10);
bool verbose = arg_get_lit(ctx, 11);
bool load_default = ! arg_get_lit(ctx, 11);
bool verbose = arg_get_lit(ctx, 12);
CLIParserFree(ctx);
// sanity checks
if (vkeylen && (vkeylen != AES_KEY_LEN)) {
PrintAndLogEx(ERR, "Specified key must have 16 bytes. Got %d", vkeylen);
return PM3_EINVARG;
}
if (pattern1b && pattern2b) {
PrintAndLogEx(ERR, "Pattern search mode must be 2-byte or 1-byte only");
return PM3_EINVARG;
}
if (fnlen && (pattern1b || pattern2b)) {
PrintAndLogEx(ERR, "Pattern search mode and dictionary mode can't be used in one command");
return PM3_EINVARG;
}
if (vpatternlen && pattern2b == false) {
PrintAndLogEx(WARNING, "Pattern entered, but search mode not is 2-byte search");
return PM3_EINVARG;
}
if (vpatternlen > 2) {
PrintAndLogEx(ERR, "Pattern must be 2-bytes. Got %d", vpatternlen);
return PM3_EINVARG;
}
uint32_t startPattern = (vpattern[0] << 8) + vpattern[1];
// read card UID
iso14a_card_select_t card;
int nxptype = MTNONE;
int res = mfp_read_card_id(&card, &nxptype);
if (res != PM3_SUCCESS) {
return res;
}
uint8_t startKeyAB = 0;
uint8_t endKeyAB = 1;
if (keyA && (keyB == false)) {
@ -1524,14 +1717,38 @@ static int CmdHFMFPChk(const char *Cmd) {
endSector = startSector;
}
// Print generic information
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, "--- " _CYAN_("Check keys") " ----------");
PrintAndLogEx(INFO, "Start sector... %u", startSector);
PrintAndLogEx(INFO, "End sector..... %u", endSector);
char keytypestr[6] = {0};
if (keyA == false && keyB == false) {
strcat(keytypestr, "AB");
}
if (keyA) {
strcat(keytypestr, "A");
}
if (keyB) {
strcat(keytypestr, "B");
}
PrintAndLogEx(INFO, "Key type....... " _YELLOW_("%s"), keytypestr);
PrintAndLogEx(NORMAL, "");
//
// Key creation section
//
// 1-byte pattern search mode
if (pattern1b) {
for (int i = 0; i < 0x100; i++) {
memset(keyList[i], i, 16);
PrintAndLogEx(DEBUG, _YELLOW_("%3d") " - %s", i, sprint_hex_inrow(keyList[i], AES_KEY_LEN));
}
keyListLen = 0x100;
PrintAndLogEx(SUCCESS, "loaded " _GREEN_("%d") " pattern1b keys", 0x100);
}
// 2-byte pattern search mode
@ -1539,82 +1756,55 @@ static int CmdHFMFPChk(const char *Cmd) {
Fill2bPattern(keyList, &keyListLen, &startPattern);
}
int res = PM3_SUCCESS;
// dictionary mode
size_t endFilePosition = 0;
if (dict_filenamelen) {
uint8_t *key_block = NULL;
uint32_t keycnt = 0;
uint32_t keycnt = 0;
res = loadFileDICTIONARYEx((char *)dict_filename, keyList, sizeof(keyList), NULL, 16, &keycnt, 0, &endFilePosition, true);
if (res == PM3_SUCCESS && endFilePosition) {
keyListLen = keycnt;
PrintAndLogEx(SUCCESS, "First part of dictionary successfully loaded.");
}
int ret = mfp_load_keys(&key_block, &keycnt, vkey, vkeylen, filename, fnlen, card.uid, load_default);
if (ret != PM3_SUCCESS) {
return ret;
}
if (keyListLen == 0) {
for (int i = 0; i < g_mifare_plus_default_keys_len; i++) {
if (hex_to_bytes(g_mifare_plus_default_keys[i], keyList[keyListLen], 16) != 16) {
break;
}
PrintAndLogEx(INFO, "Start check for keys...");
keyListLen++;
}
// time
uint64_t t1 = msclock();
res = plus_key_check(startSector, endSector, startKeyAB, endKeyAB, key_block, keycnt, foundKeys, verbose, true);
if (res == PM3_EOPABORTED) {
t1 = msclock() - t1;
PrintAndLogEx(INFO, "\ntime in checkkeys " _YELLOW_("%.0f") " seconds\n", (float)t1 / 1000.0);
return res;
}
if (keyListLen == 0) {
PrintAndLogEx(ERR, "Key list is empty. Nothing to check.");
return PM3_EINVARG;
} else {
PrintAndLogEx(INFO, "Loaded " _YELLOW_("%"PRIu32) " keys", keyListLen);
}
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, "Start check for pattern based keys...");
while (keyListLen) {
if (verbose == false) {
PrintAndLogEx(INFO, "Search keys");
}
while (true) {
res = plus_key_check(startSector, endSector, startKeyAB, endKeyAB, keyList, keyListLen, foundKeys, verbose);
res = plus_key_check(startSector, endSector, startKeyAB, endKeyAB, (uint8_t *)keyList, keyListLen, foundKeys, verbose, true);
if (res == PM3_EOPABORTED) {
break;
}
if (pattern2b && startPattern < 0x10000) {
if (verbose == false) {
PrintAndLogEx(NORMAL, "p" NOLF);
}
keyListLen = 0;
PrintAndLogEx(NORMAL, "");
Fill2bPattern(keyList, &keyListLen, &startPattern);
continue;
}
if (dict_filenamelen && endFilePosition) {
if (verbose == false)
PrintAndLogEx(NORMAL, "d" NOLF);
uint32_t keycnt = 0;
res = loadFileDICTIONARYEx((char *)dict_filename, keyList, sizeof(keyList), NULL, 16, &keycnt, endFilePosition, &endFilePosition, false);
if (res == PM3_SUCCESS && endFilePosition) {
keyListLen = keycnt;
}
continue;
}
break;
}
if (verbose == false) {
PrintAndLogEx(NORMAL, "");
}
t1 = msclock() - t1;
PrintAndLogEx(INFO, "\ntime in checkkeys " _YELLOW_("%.0f") " seconds\n", (float)t1 / 1000.0);
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(SUCCESS, _GREEN_("found keys:"));
// print result
char strA[46 + 1] = {0};
char strB[46 + 1] = {0};
bool has_ndef_key = false;
bool printedHeader = false;
for (uint8_t s = startSector; s <= endSector; s++) {
@ -1662,29 +1852,12 @@ static int CmdHFMFPChk(const char *Cmd) {
uint8_t data[10 + 1 + 2 + 1 + 256 + keys_len];
memset(data, 0, sizeof(data));
// Mifare Plus info
SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT, 0, 0, NULL, 0);
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) {
PrintAndLogEx(WARNING, "timeout while waiting for reply");
return PM3_ETIMEOUT;
}
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
uint8_t atslen = 0;
if (select_status == 1 || select_status == 2) {
memcpy(data, card.uid, card.uidlen);
data[10] = card.sak;
data[11] = card.atqa[1];
data[12] = card.atqa[0];
atslen = card.ats_len;
data[13] = atslen;
memcpy(&data[14], card.ats, atslen);
}
memcpy(data, card.uid, card.uidlen);
data[10] = card.sak;
data[11] = card.atqa[1];
data[12] = card.atqa[0];
data[13] = card.ats_len;
memcpy(&data[14], card.ats, card.ats_len);
char *fptr = calloc(sizeof(char) * (strlen("hf-mfp-") + strlen("-key")) + card.uidlen * 2 + 1, sizeof(uint8_t));
if (fptr == NULL) {
@ -1696,7 +1869,7 @@ static int CmdHFMFPChk(const char *Cmd) {
FillFileNameByUID(fptr, card.uid, "-key", card.uidlen);
// length: UID(10b)+SAK(1b)+ATQA(2b)+ATSlen(1b)+ATS(atslen)+foundKeys[2][64][AES_KEY_LEN + 1]
memcpy(&data[14 + atslen], foundKeys, keys_len);
memcpy(&data[14 + card.ats_len], foundKeys, keys_len);
// 64 here is for how many "rows" there is in the data array. A bit confusing
saveFileJSON(fptr, jsfMfPlusKeys, data, 64, NULL);
free(fptr);

View file

@ -19,20 +19,7 @@
#define CMDHFMFP_H__
#include "common.h"
typedef enum {
MFP_UNKNOWN = 0,
DESFIRE_MF3ICD40,
DESFIRE_EV1,
DESFIRE_EV2,
DESFIRE_EV2_XL,
DESFIRE_EV3,
DESFIRE_LIGHT,
PLUS_EV1,
PLUS_EV2,
NTAG413DNA,
NTAG424
} nxp_cardtype_t;
#include "mifare/mifare4.h"
typedef struct mfp_key_item {
uint8_t a[16];
@ -46,5 +33,5 @@ typedef struct mfp_keys {
int CmdHFMFP(const char *Cmd);
int CmdHFMFPNDEFRead(const char *Cmd);
int mfp_data_crypt(mf4Session_t *mf4session, uint8_t *dati, uint8_t *dato, bool rev);
#endif

View file

@ -2221,6 +2221,8 @@ int loadFileDICTIONARY(const char *preferredName, void *data, size_t *datalen, u
return loadFileDICTIONARYEx(preferredName, data, 0, datalen, keylen, keycnt, 0, NULL, true);
}
// this function handles exceptional large dictionaries,
// using start position and end position parameters.
int loadFileDICTIONARYEx(const char *preferredName, void *data, size_t maxdatalen, size_t *datalen, uint8_t keylen, uint32_t *keycnt,
size_t startFilePosition, size_t *endFilePosition, bool verbose) {
@ -2246,17 +2248,17 @@ int loadFileDICTIONARYEx(const char *preferredName, void *data, size_t maxdatale
int retval = PM3_SUCCESS;
FILE *f = fopen(path, "r");
if (!f) {
if (f == NULL) {
PrintAndLogEx(WARNING, "file not found or locked `" _YELLOW_("%s") "`", path);
retval = PM3_EFILE;
goto out;
free(path);
return PM3_EFILE;
}
if (startFilePosition) {
if (fseek(f, startFilePosition, SEEK_SET) < 0) {
fclose(f);
retval = PM3_EFILE;
goto out;
free(path);
return PM3_EFILE;
}
}
@ -2264,6 +2266,7 @@ int loadFileDICTIONARYEx(const char *preferredName, void *data, size_t maxdatale
// read file
while (!feof(f)) {
long filepos = ftell(f);
if (!fgets(line, sizeof(line), f)) {
@ -2321,7 +2324,7 @@ int loadFileDICTIONARYEx(const char *preferredName, void *data, size_t maxdatale
if (keycnt) {
*keycnt = vkeycnt;
}
out:
free(path);
return retval;
}
@ -2346,11 +2349,10 @@ int loadFileDICTIONARY_safe_ex(const char *preferredName, const char *suffix, vo
// mf desfire == 3des3k 24 bytes
// iclass == 8 bytes
// default to 6 bytes.
if (keylen != 4 && keylen != 5 && keylen != 6 && keylen != 8 && keylen != 16 && keylen != 24) {
if (keylen != 4 && keylen != 5 && keylen != 6 && keylen != 8 && keylen != 12 && keylen != 16 && keylen != 24) {
keylen = 6;
}
size_t mem_size;
size_t block_size = 10 * keylen;
// double up since its chars
@ -2365,10 +2367,10 @@ int loadFileDICTIONARY_safe_ex(const char *preferredName, const char *suffix, vo
free(path);
return PM3_EFILE;
}
mem_size = block_size;
size_t mem_size = block_size;
FILE *f = fopen(path, "r");
if (!f) {
if (f == NULL) {
PrintAndLogEx(WARNING, "file not found or locked `" _YELLOW_("%s") "`", path);
retval = PM3_EFILE;
goto out;
@ -2400,6 +2402,7 @@ int loadFileDICTIONARY_safe_ex(const char *preferredName, const char *suffix, vo
// remove newline/linefeed
str_cleanrn(line, strlen(line));
str_trim(line);
// smaller keys than expected is skipped
if (strlen(line) < keylen) {
@ -2417,6 +2420,7 @@ int loadFileDICTIONARY_safe_ex(const char *preferredName, const char *suffix, vo
// larger keys than expected is skipped
if (strlen(line) > keylen) {
PrintAndLogEx(INFO, "too long line (%zu) ... %s", strlen(line), line);
continue;
}