mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2025-08-21 13:53:55 -07:00
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:
parent
15fbfafac1
commit
5f2edb9bb8
11 changed files with 996 additions and 443 deletions
|
@ -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"));
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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!");
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue