Add MIFARE Plus commands in trace list

This commit is contained in:
DidierA 2023-07-19 21:15:07 +02:00
parent 0d682da676
commit b314d1ef1c
7 changed files with 300 additions and 12 deletions

View file

@ -3,6 +3,7 @@ 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]
- Added `hf mfp list` - interprets MIFARE Plus commands in traces (@DidierA)
- Changed `hf legic sim` - loop and return codes on deviceside updated to DEFINES (@iceman1001)
- Changed `hf legic einfo` - now accepts the three different cardsizes as params (@iceman1001)
- Fix `lf cotag reader -1` - now doesn't fail (@iceman1001)

View file

@ -1196,6 +1196,230 @@ void annotateMfDesfire(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize) {
}
}
}
// codes for which no data is interpreted, returns the message to print.
const char *mfpGetAnnotationForCode(uint8_t code) {
struct mfp_code_msg {
uint8_t code;
const char *annotation;
} messages[] = {
{ MFP_GETVERSION, "GET VERSION"},
{ MFP_ADDITIONALFRAME, "NEXT FRAME"},
{ MFP_AUTHENTICATENONFIRST, "FOLLOWING AUTH"},
{ MFP_AUTHENTICATECONTINUE, "SECOND AUTH STEP"},
{ MFP_RESETAUTH, "RESET AUTH"},
{ MFP_COMMITPERSO, "COMMIT PERSO"},
{ MFP_VCSUPPORTLASTISOL3, "CHECK VIRTUAL CARD"},
{ MFP_ISOSELECT, "SELECT VIRTUAL CARD"},
{ MFP_SETCONFIGSL1, "SET CONFIG SL1"},
{ MFP_MF_PERSONALIZEUIDUSAGE, "PERSONALIZE UID USAGE"},
{ MFP_READ_SIG, "READ SIGNATURE"},
{ MFDES_PREPARE_PC, "PREPARE PROXIMITY CHECK"},
{ MFDES_PROXIMITY_CHECK, "PROXIMITY CHECK"},
{ MFDES_VERIFY_PC, "VERIFY PROXIMITY CHECK"},
{ 0, NULL}
} ;
for (struct mfp_code_msg *p=messages ; p->annotation != NULL ; p++) {
if (p->code == code) {
return p->annotation ;
}
}
return NULL ;
}
// MIFARE Plus
void annotateMfPlus(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize) {
// If we are in Mifare Classic Authenticated mode, all the work has already be done elsewhere
if ((MifareAuthState != masNone) && (MifareAuthState != masError)) {
return ;
}
// it's basically a ISO14443a tag, so try annotation from there
if (applyIso14443a(exp, size, cmd, cmdsize, false) == PM3_SUCCESS) {
return ;
}
// ok this part is copy paste from annotateMfDesfire, it seems to work for MIFARE Plus also
if (((cmd[0] & 0xC0) == 0x00) && (cmdsize > 2)) {
// PCB [CID] [NAD] [INF] CRC CRC
int pos = 1;
if ((cmd[0] & 0x08) == 0x08) // cid byte following
pos++;
if ((cmd[0] & 0x04) == 0x04) // nad byte following
pos++;
for (uint8_t i = 0; i < 2; i++, pos++) {
bool found_annotation = true;
uint8_t *data = cmd + pos + 1;
// if the byte prior to the command is 90 the command is wrapped, so data starts 3 bytes later
if (i > 0 && cmd[pos - 1] == 0x90) {
data += 3;
}
uint8_t data_size = 0;
if (cmdsize > (data - cmd)) {
data_size = cmdsize - (data - cmd);
}
// Messages for commands that do not need args are treated first
const char *annotation = mfpGetAnnotationForCode(cmd[pos]) ;
if (annotation != NULL) {
snprintf(exp, size, "%s", annotation) ;
break ;
}
switch (cmd[pos]) {
case MFP_AUTHENTICATEFIRST:
case MFP_AUTHENTICATEFIRST_VARIANT:
if (data_size > 1) {
// key : uint16_t uKeyNum = 0x4000 + sectorNum * 2 + (keyB ? 1 : 0);
uint16_t uKeyNum = MemLeToUint2byte(data) ;
snprintf(exp, size, "FIRST AUTH (Keynr 0x%04X: %c sector %d)", uKeyNum, uKeyNum & 0x0001 ? 'B' : 'A', (uKeyNum - 0x4000)/2 );
} else {
snprintf(exp, size, "FIRST AUTH") ;
}
break;
case MFP_WRITEPERSO:
if (data_size > 1) {
uint16_t uKeyNum = MemLeToUint2byte(data) ;
snprintf(exp, size, "WRITE PERSO (Keynr 0x%04X)", uKeyNum);
} else {
snprintf(exp, size, "WRITE PERSO");
}
break;
case MFP_READENCRYPTEDNOMAC_MACED:
case MFP_READENCRYPTEDMAC_MACED:
case MFP_READENCRYPTEDNOMAC_UNMACED:
case MFP_READENCRYPTEDMAC_UNMACED:
if (data_size > 2) {
uint16_t uBlockNum = MemLeToUint2byte(data) ;
uint8_t uQty = data[2] ;
if (uQty != 1) {
snprintf(exp, size, "READ ENCRYPTED(%u-%u)", uBlockNum, uBlockNum+uQty-1);
} else {
snprintf(exp, size, "READ ENCRYPTED(%u)", uBlockNum);
}
} else {
snprintf(exp, size, "READ ENCRYPTED ?");
}
break;
case MFP_READPLAINNOMAC_MACED:
case MFP_READPLAINMAC_MACED:
case MFP_READPLAINNOMAC_UNMACED:
case MFP_READPLAINMAC_UNMACED:
if (data_size > 2) {
uint16_t uBlockNum = MemLeToUint2byte(data) ;
uint8_t uQty = data[2] ;
if (uQty != 1) {
snprintf(exp, size, "READ PLAIN(%u-%u)", uBlockNum, uBlockNum+uQty-1);
} else {
snprintf(exp, size, "READ PLAIN(%u)", uBlockNum);
}
} else {
snprintf(exp, size, "READ PLAIN ?");
}
break;
case MFP_WRITEPLAINNOMAC :
case MFP_WRITEPLAINMAC :
if (data_size > 1) {
uint16_t uBlockNum = MemLeToUint2byte(data) ;
snprintf(exp, size, "WRITE PLAIN(%u)", uBlockNum);
} else {
snprintf(exp, size, "WRITE PLAIN ?");
}
break;
case MFP_WRITEENCRYPTEDNOMAC:
case MFP_WRITEENCRYPTEDMAC :
if (data_size > 1) {
uint16_t uBlockNum = MemLeToUint2byte(data) ;
snprintf(exp, size, "WRITE ENCRYPTED(%u)", uBlockNum);
} else {
snprintf(exp, size, "WRITE ENCRYPTED ?");
}
break;
case MFP_INCREMENTNOMAC :
case MFP_INCREMENTMAC :
if (data_size > 1) {
uint16_t uBlockNum = MemLeToUint2byte(data) ;
snprintf(exp, size, "INCREMENT(%u)", uBlockNum);
} else {
snprintf(exp, size, "INCREMENT ?");
}
break;
case MFP_DECREMENTNOMAC :
case MFP_DECREMENTMAC :
if (data_size > 1) {
uint16_t uBlockNum = MemLeToUint2byte(data) ;
snprintf(exp, size, "DECREMENT(%u)", uBlockNum);
} else {
snprintf(exp, size, "DECREMENT ?");
}
break;
case MFP_TRANSFERNOMAC :
case MFP_TRANSFERMAC :
if (data_size > 1) {
uint16_t uBlockNum = MemLeToUint2byte(data) ;
snprintf(exp, size, "TRANSFER(%u)", uBlockNum);
} else {
snprintf(exp, size, "TRANSFER ?");
}
break;
case MFP_INCREMENTTRANSFERNOMAC:
case MFP_INCREMENTTRANSFERMAC :
if (data_size > 1) {
uint16_t uBlockNum = MemLeToUint2byte(data) ;
snprintf(exp, size, "INCREMENT, TRANSFER(%u)", uBlockNum);
} else {
snprintf(exp, size, "INCREMENT, TRANSFER ?");
}
break;
case MFP_DECREMENTTRANSFERNOMAC:
case MFP_DECREMENTTRANSFERMAC :
if (data_size > 1) {
uint16_t uBlockNum = MemLeToUint2byte(data) ;
snprintf(exp, size, "DECREMENT, TRANSFER(%u)", uBlockNum);
} else {
snprintf(exp, size, "DECREMENT, TRANSFER ?");
}
break;
case MFP_RESTORENOMAC :
case MFP_RESTOREMAC :
if (data_size > 1) {
uint16_t uBlockNum = MemLeToUint2byte(data) ;
snprintf(exp, size, "RESTORE(%u)", uBlockNum);
} else {
snprintf(exp, size, "RESTORE ?");
}
break;
default:
found_annotation = false;
break;
}
if (found_annotation) {
break;
}
}
} else {
// anything else
snprintf(exp, size, "?");
}
}
/**
06 00 = INITIATE

View file

@ -56,6 +56,8 @@ void annotateIso7816(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize);
void annotateIso14443b(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize);
void annotateIso14443a(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize, bool is_response);
void annotateMfDesfire(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize);
const char *mfpGetAnnotationForCode(uint8_t code);
void annotateMfPlus(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize);
void annotateMifare(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize,
const uint8_t *parity, uint8_t paritysize, bool isResponse);
void annotateLTO(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize);

View file

@ -1811,7 +1811,7 @@ static int CmdHFMFPNDEFWrite(const char *Cmd) {
}
static int CmdHFMFPList(const char *Cmd) {
return CmdTraceListAlias(Cmd, "hf mf", "mf");
return CmdTraceListAlias(Cmd, "hf mfp", "mfp");
}
static command_t CommandTable[] = {

View file

@ -533,6 +533,7 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr
crcStatus = !felica_CRC_check(frame + 2, data_len - 4);
break;
case PROTO_MIFARE:
case PROTO_MFPLUS:
crcStatus = mifare_CRC_check(hdr->isResponse, frame, data_len);
break;
case ISO_14443A:
@ -603,7 +604,7 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr
&& protocol != FELICA
&& protocol != LTO
&& protocol != PROTO_CRYPTORF
&& (hdr->isResponse || protocol == ISO_14443A || protocol == PROTO_MIFARE || protocol == SEOS)
&& (hdr->isResponse || protocol == ISO_14443A || protocol == PROTO_MIFARE || protocol == PROTO_MFPLUS || protocol == SEOS)
&& (oddparity8(frame[j]) != ((parityBits >> (7 - (j & 0x0007))) & 0x01))) {
snprintf(line[j / 18] + ((j % 18) * 4), 120, "%02x! ", frame[j]);
@ -701,6 +702,7 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr
// mark short bytes (less than 8 Bit + Parity)
if (protocol == ISO_14443A ||
protocol == PROTO_MIFARE ||
protocol == PROTO_MFPLUS ||
protocol == THINFILM) {
// approximated with 128 * (9 * data_len);
@ -747,6 +749,7 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr
annotateIso14443a(explanation, sizeof(explanation), frame, data_len, hdr->isResponse);
break;
case PROTO_MIFARE:
case PROTO_MFPLUS:
annotateMifare(explanation, sizeof(explanation), frame, data_len, parityBytes, TRACELOG_PARITY_LEN(hdr), hdr->isResponse);
break;
case PROTO_HITAG1:
@ -766,7 +769,6 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr
}
if (hdr->isResponse == false) {
switch (protocol) {
case LEGIC:
annotateLegic(explanation, sizeof(explanation), frame, data_len);
@ -774,6 +776,9 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr
case MFDES:
annotateMfDesfire(explanation, sizeof(explanation), frame, data_len);
break;
case PROTO_MFPLUS:
annotateMfPlus(explanation, sizeof(explanation), frame, data_len);
break;
case ISO_14443B:
annotateIso14443b(explanation, sizeof(explanation), frame, data_len);
break;
@ -901,7 +906,7 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr
}
}
if (protocol == PROTO_MIFARE) {
if (protocol == PROTO_MIFARE || protocol == PROTO_MFPLUS) {
uint8_t mfData[32] = {0};
size_t mfDataLen = 0;
if (DecodeMifareData(frame, data_len, parityBytes, hdr->isResponse, mfData, &mfDataLen, mfDicKeys, mfDicKeysCount)) {
@ -1200,6 +1205,7 @@ int CmdTraceList(const char *Cmd) {
"trace list -t seos -> interpret as " _YELLOW_("SEOS") "\n"
"trace list -t thinfilm -> interpret as " _YELLOW_("Thinfilm") "\n"
"trace list -t topaz -> interpret as " _YELLOW_("Topaz") "\n"
"trace list -t mfp -> interpret as " _YELLOW_("MIFARE Plus") "\n"
"\n"
"trace list -t mf -f mfc_default_keys.dic -> use default dictionary file\n"
"trace list -t 14a --frame -> show frame delay times\n"
@ -1266,6 +1272,7 @@ int CmdTraceList(const char *Cmd) {
else if (strcmp(type, "seos") == 0) protocol = SEOS;
else if (strcmp(type, "thinfilm") == 0) protocol = THINFILM;
else if (strcmp(type, "topaz") == 0) protocol = TOPAZ;
else if (strcmp(type, "mfp") == 0) protocol = PROTO_MFPLUS;
else if (strcmp(type, "") == 0) protocol = -1;
else {
PrintAndLogEx(FAILED, "Unknown protocol \"%s\"", type);
@ -1304,7 +1311,7 @@ int CmdTraceList(const char *Cmd) {
PrintAndLogEx(INFO, _YELLOW_("start") " = start of start frame " _YELLOW_("end") " = end of frame. " _YELLOW_("src") " = source of transfer");
}
if (protocol == ISO_14443A || protocol == PROTO_MIFARE || protocol == MFDES || protocol == TOPAZ || protocol == LTO) {
if (protocol == ISO_14443A || protocol == PROTO_MIFARE || protocol == MFDES || protocol == PROTO_MFPLUS || protocol == TOPAZ || protocol == LTO) {
if (use_us)
PrintAndLogEx(INFO, _YELLOW_("ISO14443A") " - all times are in microseconds");
else
@ -1354,7 +1361,7 @@ int CmdTraceList(const char *Cmd) {
uint32_t dicKeysCount = 0;
bool dictionaryLoad = false;
if (protocol == PROTO_MIFARE) {
if (protocol == PROTO_MIFARE || protocol == PROTO_MFPLUS) {
if (diclen > 0) {
uint8_t *keyBlock = NULL;
int res = loadFileDICTIONARY_safe(dictionary, (void **) &keyBlock, 6, &dicKeysCount);
@ -1387,7 +1394,7 @@ int CmdTraceList(const char *Cmd) {
PrintAndLogEx(NORMAL, "------------+------------+-----+-------------------------------------------------------------------------+-----+--------------------");
// clean authentication data used with the mifare classic decrypt fct
if (protocol == ISO_14443A || protocol == PROTO_MIFARE)
if (protocol == ISO_14443A || protocol == PROTO_MIFARE || protocol == PROTO_MFPLUS)
ClearAuthData();
uint32_t previous_EOT = 0;

View file

@ -6159,10 +6159,10 @@
},
"hf mfp list": {
"command": "hf mfp list",
"description": "Alias of `trace list -t mf` with selected protocol data to annotate trace buffer You can load a trace from file (see `trace load -h`) or it be downloaded from device by default It accepts all other arguments of `trace list`. Note that some might not be relevant for this specific protocol",
"description": "Alias of `trace list -t mfp` with selected protocol data to annotate trace buffer You can load a trace from file (see `trace load -h`) or it be downloaded from device by default It accepts all other arguments of `trace list`. Note that some might not be relevant for this specific protocol",
"notes": [
"hf mf list --frame -> show frame delay times",
"hf mf list -1 -> use trace buffer"
"hf mfp list --frame -> show frame delay times",
"hf mfp list -1 -> use trace buffer"
],
"offline": true,
"options": [
@ -6176,7 +6176,7 @@
"or to import into Wireshark using encapsulation type \"ISO 14443\"",
"-f, --file <fn> filename of dictionary"
],
"usage": "hf mf list [-h1crux] [--frame] [-f <fn>]"
"usage": "hf mfp list [-h1crux] [--frame] [-f <fn>]"
},
"hf mfp mad": {
"command": "hf mfp mad",
@ -11488,6 +11488,7 @@
"trace list -t seos -> interpret as SEOS",
"trace list -t thinfilm -> interpret as Thinfilm",
"trace list -t topaz -> interpret as Topaz",
"trace list -t mfp -> interpret as MIFARE Plus",
"",
"trace list -t mf -f mfc_default_keys.dic -> use default dictionary file",
"trace list -t 14a --frame -> show frame delay times",
@ -11694,6 +11695,6 @@
"metadata": {
"commands_extracted": 679,
"extracted_by": "PM3Help2JSON v1.00",
"extracted_on": "2023-07-17T15:46:12"
"extracted_on": "2023-07-19T19:11:10"
}
}

View file

@ -428,6 +428,7 @@ ISO 7816-4 Basic interindustry commands. For command APDU's.
#define PROTO_HITAGS 14
#define PROTO_CRYPTORF 15
#define SEOS 16
#define PROTO_MFPLUS 17
// Picopass fuses
#define FUSE_FPERS 0x80
@ -620,6 +621,58 @@ ISO 7816-4 Basic interindustry commands. For command APDU's.
#define MFDES_E_FILE_NOT_FOUND 0xF0
#define MFDES_E_FILE_INTEGRITY 0xF1
// MIFARE PLus EV2 Command set
// source: https://www.nxp.com/docs/en/data-sheet/MF1P(H)x2.pdf in Look-Up Tables
#define MFP_READ_SIG 0x3C // same as DESFIRE
#define MFP_WRITEPERSO 0xA8
#define MFP_COMMITPERSO 0xAA
#define MFP_AUTHENTICATEFIRST 0x70
#define MFP_AUTHENTICATEFIRST_VARIANT 0x73
#define MFP_AUTHENTICATENONFIRST 0x76
#define MFP_AUTHENTICATECONTINUE 0x72
#define MFP_AUTHENTICATESECTORSWITCH 0x7A
#define MFP_RESETAUTH 0x78
#define MFP_VCSUPPORTLASTISOL3 0x4B
#define MFP_ISOSELECT 0xA4
#define MFP_GETVERSION 0x60 // same as DESFIRE
#define MFP_ADDITIONALFRAME 0xAF
#define MFP_SETCONFIGSL1 0x44
#define MFP_MF_PERSONALIZEUIDUSAGE 0x40
// read commands
#define MFP_READENCRYPTEDNOMAC_MACED 0X30
#define MFP_READENCRYPTEDMAC_MACED 0x31
#define MFP_READPLAINNOMAC_MACED 0x32
#define MFP_READPLAINMAC_MACED 0x33
#define MFP_READENCRYPTEDNOMAC_UNMACED 0x34
#define MFP_READENCRYPTEDMAC_UNMACED 0X35
#define MFP_READPLAINNOMAC_UNMACED 0x36
#define MFP_READPLAINMAC_UNMACED 0x37
// write commands
#define MFP_WRITEENCRYPTEDNOMAC 0xA0
#define MFP_WRITEENCRYPTEDMAC 0xA1
#define MFP_WRITEPLAINNOMAC 0xA2
#define MFP_WRITEPLAINMAC 0xA3
// value commands
#define MFP_INCREMENTNOMAC 0xB0
#define MFP_INCREMENTMAC 0xB1
#define MFP_DECREMENTNOMAC 0xB2
#define MFP_DECREMENTMAC 0xB3
#define MFP_TRANSFERNOMAC 0xB4
#define MFP_TRANSFERMAC 0xB5
#define MFP_INCREMENTTRANSFERNOMAC 0xB6
#define MFP_INCREMENTTRANSFERMAC 0xB7
#define MFP_DECREMENTTRANSFERNOMAC 0xB8
#define MFP_DECREMENTTRANSFERMAC 0xB9
#define MFP_RESTORENOMAC 0xC2
#define MFP_RESTOREMAC 0xC3
// LEGIC Commands
#define LEGIC_MIM_22 0x0D