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:
parent
ddd7cabda8
commit
b1448f6785
2 changed files with 50 additions and 23 deletions
|
@ -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)
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue