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
parent 15fbfafac1
commit 5f2edb9bb8
11 changed files with 996 additions and 443 deletions
+4
View File
@@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file.
This project uses the changelog in accordance with [keepchangelog](http://keepachangelog.com/). Please use this to write notable changes, which is not the same as git commit log...
## [unreleased][unreleased]
- Changed `hf 14a info` - refactored code to be able to detect card technology across the client easier (@iceman1001)
- Changed `hf mf info` - now informs better if a different card technology is detected (@iceman1001)
- Changed `hf mf autopwn` - now exits if desfire is detected and limit attacks if mifare plus is detected (@iceman1001)
- Changed `hf mfp chk` - improved key handling and output (@iceman1001)
- Fix `hf mf dump` - added a check for keyfile to contain enough keys for card (@iceman1001)
- Fix `hf mf eview` - now viewing 2k, 4k cards doesn't get wrong background color (@iceman1001)
- Changed `hf mf info` - skip checking if it detects a MIFARE Ultralight family card (@iceman1001)
+291 -108
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"));
}
+19 -1
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
+213 -31
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));
}
+4
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
+4 -28
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!");
+40 -19
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
+401 -228
View File
File diff suppressed because it is too large Load Diff
+2 -15
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
+14 -10
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;
}
+4 -3
View File
@@ -6806,7 +6806,7 @@
"notes": [
"hf mfp chk -k 000102030405060708090a0b0c0d0e0f -> check key on sector 0 as key A and B",
"hf mfp chk -s 2 -a -> check default key list on sector 2, only key A",
"hf mfp chk -f mfp_default_keys -s0 -e6 -> check keys from dictionary against sectors 0-6",
"hf mfp chk -f mfp_default_keys -s 0 -e 6 -> check keys from dictionary against sectors 0-6",
"hf mfp chk --pattern1b --dump -> check all 1-byte keys pattern and save found keys to file",
"hf mfp chk --pattern2b --startp2b FA00 -> check all 2-byte keys pattern. Start from key FA00FA00...FA00"
],
@@ -6823,9 +6823,10 @@
"--pattern2b Check all 2-byte combinations of key (0000...0000, 0001...0001, 0002...0002, ...)",
"--startp2b <pattern> Start key (2-byte HEX) for 2-byte search (use with `--pattern2b`)",
"--dump Dump found keys to JSON file",
"--no-default Skip check default keys",
"-v, --verbose Verbose output"
],
"usage": "hf mfp chk [-habv] [-s <0..255>] [-e <0..255>] [-k <hex>] [-f <fn>] [--pattern1b] [--pattern2b] [--startp2b <pattern>] [--dump]"
"usage": "hf mfp chk [-habv] [-s <0..255>] [-e <0..255>] [-k <hex>] [-f <fn>] [--pattern1b] [--pattern2b] [--startp2b <pattern>] [--dump] [--no-default]"
},
"hf mfp chkey": {
"command": "hf mfp chkey",
@@ -13376,6 +13377,6 @@
"metadata": {
"commands_extracted": 768,
"extracted_by": "PM3Help2JSON v1.00",
"extracted_on": "2025-06-08T19:44:35"
"extracted_on": "2025-06-09T12:58:22"
}
}