nfc-mfclassic: add option to tolerate RW errors & other enhancements

Reconciliate read & write operations.
Add option to tolerate or not RW failures.
Print success/failure for each block as ACL is per block.
This commit is contained in:
Philippe Teuwen 2013-03-02 22:29:10 +01:00
parent ddd7cabda8
commit b1448f6785
2 changed files with 50 additions and 23 deletions

View file

@ -4,7 +4,7 @@ nfc-mfclassic \- MIFARE Classic command line tool
.SH SYNOPSIS .SH SYNOPSIS
.B nfc-mfclassic .B nfc-mfclassic
.RI \fR\fBr\fR|\fR\fBR\fR|\fBw\fR\fR|\fBW\fR .RI \fR\fBr\fR|\fR\fBR\fR|\fBw\fR\fR|\fBW\fR
.RI \fR\fBa\fR|\fBb\fR .RI \fR\fBa\fR|\fR\fBA\fR|\fBb\fR\fR|\fBB\fR
.IR DUMP .IR DUMP
.IR [KEYS] .IR [KEYS]
@ -27,16 +27,36 @@ to store the keys and data for all sectors.
Be cautious that some parts of a MIFARE Classic memory are used for r/w access Be cautious that some parts of a MIFARE Classic memory are used for r/w access
of the rest of the memory, so please read the tag documentation before experimenting too much! of the rest of the memory, so please read the tag documentation before experimenting too much!
The 'W' option allows writing of special MIFARE cards that can be 'unlocked' to allow block 0 The
.B W
option allows writing of special MIFARE cards that can be 'unlocked' to allow block 0
to be overwritten. This includes UID and manufacturer data. Take care when amending UIDs to set to be overwritten. This includes UID and manufacturer data. Take care when amending UIDs to set
the correct BCC (UID checksum). Currently only 4 byte UIDs are supported. the correct BCC (UID checksum). Currently only 4 byte UIDs are supported.
Similarly, the 'R' option allows an 'unlocked' read. This bypasses authentication and allows Similarly, the
.B R
option allows an 'unlocked' read. This bypasses authentication and allows
reading of the Key A and Key B data regardless of ACLs. reading of the Key A and Key B data regardless of ACLs.
*** Note that 'W' and 'R' options only work on special versions of MIFARE 1K cards (Chinese clones). R/W errors on some blocks can be either considered as critical or ignored.
To halt on first error, specify keys with lowercase (
.B a
or
.B b
). To ignore such errors, use uppercase (
.B A
or
.B B
).
*** Note that
.B W
and
.B R
options only work on special versions of MIFARE 1K cards (Chinese clones).
.SH OPTIONS .SH OPTIONS
.TP
.BR r " | " R " | " w " | " W .BR r " | " R " | " w " | " W
Perform read from ( Perform read from (
.B r .B r
@ -48,8 +68,18 @@ Perform read from (
.B W .B W
) card. ) card.
.TP .TP
.BR a " | " b .BR a " | " A " | " b " | " B
Use A or B MIFARE keys. Use A or B MIFARE keys.
Halt on errors (
.B a
|
.B b
) or tolerate errors (
.B A
|
.B
B
).
.TP .TP
.IR DUMP .IR DUMP
MiFare Dump (MFD) used to write (card to MFD) or (MFD to card) MiFare Dump (MFD) used to write (card to MFD) or (MFD to card)

View file

@ -61,6 +61,7 @@ static mifare_classic_tag mtKeys;
static mifare_classic_tag mtDump; static mifare_classic_tag mtDump;
static bool bUseKeyA; static bool bUseKeyA;
static bool bUseKeyFile; static bool bUseKeyFile;
static bool bTolerateFailures;
static uint8_t uiBlocks; static uint8_t uiBlocks;
static uint8_t keys[] = { static uint8_t keys[] = {
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
@ -268,18 +269,11 @@ read_card(int read_unlocked)
if (!unlock_card()) if (!unlock_card())
return false; return false;
printf("Reading out %d blocks |", uiBlocks + 1); printf("Reading out %d blocks |", uiBlocks + 1);
// Read the card from end to begin // Read the card from end to begin
for (iBlock = uiBlocks; iBlock >= 0; iBlock--) { for (iBlock = uiBlocks; iBlock >= 0; iBlock--) {
// Authenticate everytime we reach a trailer block // Authenticate everytime we reach a trailer block
if (is_trailer_block(iBlock)) { if (is_trailer_block(iBlock)) {
// Skip this the first time, bFailure it means nothing (yet)
if (iBlock != uiBlocks)
print_success_or_failure(bFailure, &uiReadBlocks);
// Show if the readout went well
if (bFailure) { if (bFailure) {
// When a failure occured we need to redo the anti-collision // When a failure occured we need to redo the anti-collision
if (nfc_initiator_select_passive_target(pnd, nmMifare, NULL, 0, &nt) <= 0) { if (nfc_initiator_select_passive_target(pnd, nmMifare, NULL, 0, &nt) <= 0) {
@ -307,7 +301,8 @@ read_card(int read_unlocked)
memcpy(mtDump.amb[iBlock].mbt.abtKeyB, mtKeys.amb[iBlock].mbt.abtKeyB, 6); memcpy(mtDump.amb[iBlock].mbt.abtKeyB, mtKeys.amb[iBlock].mbt.abtKeyB, 6);
} }
} else { } else {
printf("!\nError: unable to read trailer block 0x%02x\n", iBlock); printf("!\nfailed to read trailer block 0x%02x\n", iBlock);
bFailure = true;
} }
} else { } else {
// Make sure a earlier readout did not fail // Make sure a earlier readout did not fail
@ -317,12 +312,15 @@ read_card(int read_unlocked)
memcpy(mtDump.amb[iBlock].mbd.abtData, mp.mpd.abtData, 16); memcpy(mtDump.amb[iBlock].mbd.abtData, mp.mpd.abtData, 16);
} else { } else {
printf("!\nError: unable to read block 0x%02x\n", iBlock); printf("!\nError: unable to read block 0x%02x\n", iBlock);
return false; bFailure = true;
} }
} }
} }
// Show if the readout went well for each block
print_success_or_failure(bFailure, &uiReadBlocks);
if ((! bTolerateFailures) && bFailure)
return false;
} }
print_success_or_failure(bFailure, &uiReadBlocks);
printf("|\n"); printf("|\n");
printf("Done, %d of %d blocks read.\n", uiReadBlocks, uiBlocks + 1); printf("Done, %d of %d blocks read.\n", uiReadBlocks, uiBlocks + 1);
fflush(stdout); fflush(stdout);
@ -337,7 +335,6 @@ write_card(int write_block_zero)
bool bFailure = false; bool bFailure = false;
uint32_t uiWriteBlocks = 0; uint32_t uiWriteBlocks = 0;
if (write_block_zero) if (write_block_zero)
if (!unlock_card()) if (!unlock_card())
return false; return false;
@ -347,11 +344,6 @@ write_card(int write_block_zero)
for (uiBlock = 0; uiBlock <= uiBlocks; uiBlock++) { for (uiBlock = 0; uiBlock <= uiBlocks; uiBlock++) {
// Authenticate everytime we reach the first sector of a new block // Authenticate everytime we reach the first sector of a new block
if (is_first_block(uiBlock)) { if (is_first_block(uiBlock)) {
// Skip this the first time, bFailure it means nothing (yet)
if (uiBlock != 0)
print_success_or_failure(bFailure, &uiWriteBlocks);
// Show if the readout went well
if (bFailure) { if (bFailure) {
// When a failure occured we need to redo the anti-collision // When a failure occured we need to redo the anti-collision
if (nfc_initiator_select_passive_target(pnd, nmMifare, NULL, 0, &nt) <= 0) { if (nfc_initiator_select_passive_target(pnd, nmMifare, NULL, 0, &nt) <= 0) {
@ -402,8 +394,11 @@ write_card(int write_block_zero)
bFailure = true; bFailure = true;
} }
} }
// Show if the write went well for each block
print_success_or_failure(bFailure, &uiWriteBlocks);
if ((! bTolerateFailures) && bFailure)
return false;
} }
print_success_or_failure(bFailure, &uiWriteBlocks);
printf("|\n"); printf("|\n");
printf("Done, %d of %d blocks written.\n", uiWriteBlocks, uiBlocks + 1); printf("Done, %d of %d blocks written.\n", uiWriteBlocks, uiBlocks + 1);
fflush(stdout); fflush(stdout);
@ -426,7 +421,7 @@ print_usage(const char *pcProgramName)
printf(" *** note that unlocked write will attempt to overwrite block 0 including UID\n"); printf(" *** note that unlocked write will attempt to overwrite block 0 including UID\n");
printf(" *** unlocked read does not require authentication and will reveal A and B keys\n"); printf(" *** unlocked read does not require authentication and will reveal A and B keys\n");
printf(" *** unlocking only works with special Mifare 1K cards (Chinese clones)\n"); printf(" *** unlocking only works with special Mifare 1K cards (Chinese clones)\n");
printf(" a|b - Use A or B keys for action\n"); printf(" a|A|b|B - Use A or B keys for action; Halt on errors (a|b) or tolerate errors (A|B)\n");
printf(" <dump.mfd> - MiFare Dump (MFD) used to write (card to MFD) or (MFD to card)\n"); printf(" <dump.mfd> - MiFare Dump (MFD) used to write (card to MFD) or (MFD to card)\n");
printf(" <keys.mfd> - MiFare Dump (MFD) that contain the keys (optional)\n"); printf(" <keys.mfd> - MiFare Dump (MFD) that contain the keys (optional)\n");
} }
@ -455,6 +450,7 @@ main(int argc, const char *argv[])
if (strcmp(command, "R") == 0) if (strcmp(command, "R") == 0)
unlock = 1; unlock = 1;
bUseKeyA = tolower((int)((unsigned char) * (argv[2]))) == 'a'; bUseKeyA = tolower((int)((unsigned char) * (argv[2]))) == 'a';
bTolerateFailures = tolower((int)((unsigned char) * (argv[2]))) != (int)((unsigned char) * (argv[2]));
bUseKeyFile = (argc > 4); bUseKeyFile = (argc > 4);
} else if (strcmp(command, "w") == 0 || strcmp(command, "W") == 0) { } else if (strcmp(command, "w") == 0 || strcmp(command, "W") == 0) {
if (argc < 4) { if (argc < 4) {
@ -465,6 +461,7 @@ main(int argc, const char *argv[])
if (strcmp(command, "W") == 0) if (strcmp(command, "W") == 0)
unlock = 1; unlock = 1;
bUseKeyA = tolower((int)((unsigned char) * (argv[2]))) == 'a'; bUseKeyA = tolower((int)((unsigned char) * (argv[2]))) == 'a';
bTolerateFailures = tolower((int)((unsigned char) * (argv[2]))) != (int)((unsigned char) * (argv[2]));
bUseKeyFile = (argc > 4); bUseKeyFile = (argc > 4);
} }