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
|
||||
.B nfc-mfclassic
|
||||
.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 [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
|
||||
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
|
||||
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.
|
||||
|
||||
*** 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
|
||||
.TP
|
||||
.BR r " | " R " | " w " | " W
|
||||
Perform read from (
|
||||
.B r
|
||||
|
@ -48,8 +68,18 @@ Perform read from (
|
|||
.B W
|
||||
) card.
|
||||
.TP
|
||||
.BR a " | " b
|
||||
.BR a " | " A " | " b " | " B
|
||||
Use A or B MIFARE keys.
|
||||
Halt on errors (
|
||||
.B a
|
||||
|
|
||||
.B b
|
||||
) or tolerate errors (
|
||||
.B A
|
||||
|
|
||||
.B
|
||||
B
|
||||
).
|
||||
.TP
|
||||
.IR DUMP
|
||||
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 bool bUseKeyA;
|
||||
static bool bUseKeyFile;
|
||||
static bool bTolerateFailures;
|
||||
static uint8_t uiBlocks;
|
||||
static uint8_t keys[] = {
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
|
@ -268,18 +269,11 @@ read_card(int read_unlocked)
|
|||
if (!unlock_card())
|
||||
return false;
|
||||
|
||||
|
||||
printf("Reading out %d blocks |", uiBlocks + 1);
|
||||
|
||||
// Read the card from end to begin
|
||||
for (iBlock = uiBlocks; iBlock >= 0; iBlock--) {
|
||||
// Authenticate everytime we reach a trailer block
|
||||
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) {
|
||||
// When a failure occured we need to redo the anti-collision
|
||||
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);
|
||||
}
|
||||
} 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 {
|
||||
// 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);
|
||||
} else {
|
||||
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("Done, %d of %d blocks read.\n", uiReadBlocks, uiBlocks + 1);
|
||||
fflush(stdout);
|
||||
|
@ -337,7 +335,6 @@ write_card(int write_block_zero)
|
|||
bool bFailure = false;
|
||||
uint32_t uiWriteBlocks = 0;
|
||||
|
||||
|
||||
if (write_block_zero)
|
||||
if (!unlock_card())
|
||||
return false;
|
||||
|
@ -347,11 +344,6 @@ write_card(int write_block_zero)
|
|||
for (uiBlock = 0; uiBlock <= uiBlocks; uiBlock++) {
|
||||
// Authenticate everytime we reach the first sector of a new block
|
||||
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) {
|
||||
// When a failure occured we need to redo the anti-collision
|
||||
if (nfc_initiator_select_passive_target(pnd, nmMifare, NULL, 0, &nt) <= 0) {
|
||||
|
@ -402,8 +394,11 @@ write_card(int write_block_zero)
|
|||
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("Done, %d of %d blocks written.\n", uiWriteBlocks, uiBlocks + 1);
|
||||
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(" *** 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(" 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(" <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)
|
||||
unlock = 1;
|
||||
bUseKeyA = tolower((int)((unsigned char) * (argv[2]))) == 'a';
|
||||
bTolerateFailures = tolower((int)((unsigned char) * (argv[2]))) != (int)((unsigned char) * (argv[2]));
|
||||
bUseKeyFile = (argc > 4);
|
||||
} else if (strcmp(command, "w") == 0 || strcmp(command, "W") == 0) {
|
||||
if (argc < 4) {
|
||||
|
@ -465,6 +461,7 @@ main(int argc, const char *argv[])
|
|||
if (strcmp(command, "W") == 0)
|
||||
unlock = 1;
|
||||
bUseKeyA = tolower((int)((unsigned char) * (argv[2]))) == 'a';
|
||||
bTolerateFailures = tolower((int)((unsigned char) * (argv[2]))) != (int)((unsigned char) * (argv[2]));
|
||||
bUseKeyFile = (argc > 4);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue