Unlocked read and format/wipe of special Mifare cards
This commit is contained in:
parent
1a07613ce2
commit
028f310576
4 changed files with 108 additions and 63 deletions
|
@ -3,7 +3,7 @@
|
||||||
nfc-mfclassic \- MIFARE Classic command line tool
|
nfc-mfclassic \- MIFARE Classic command line tool
|
||||||
.SH SYNOPSIS
|
.SH SYNOPSIS
|
||||||
.B nfc-mfclassic
|
.B nfc-mfclassic
|
||||||
.RI \fR\fBr\fR|\fBw\fR\fR|\fBu\fR
|
.RI \fR\fBr\fR|\fR\fBR\fR|\fBw\fR\fR|\fBW\fR
|
||||||
.RI \fR\fBa\fR|\fBb\fR
|
.RI \fR\fBa\fR|\fBb\fR
|
||||||
.IR DUMP
|
.IR DUMP
|
||||||
.IR [KEYS]
|
.IR [KEYS]
|
||||||
|
@ -27,18 +27,25 @@ 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 'u' option allows writing of special Mifare cards that can be 'unlocked' to allow block 0
|
The '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
|
||||||
|
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).
|
||||||
|
|
||||||
.SH OPTIONS
|
.SH OPTIONS
|
||||||
.BR r " | " w " | " u
|
.BR r " | " R " | " w " | " W
|
||||||
Perform read from (
|
Perform read from (
|
||||||
.B r
|
.B r
|
||||||
|
) or unlocked read from (
|
||||||
|
.B R
|
||||||
) or write to (
|
) or write to (
|
||||||
.B w
|
.B w
|
||||||
) or unlocked write to (
|
) or unlocked write to (
|
||||||
.B u
|
.B W
|
||||||
) card.
|
) card.
|
||||||
.TP
|
.TP
|
||||||
.BR a " | " b
|
.BR a " | " b
|
||||||
|
@ -65,6 +72,7 @@ are covered by the GNU Lesser General Public License (LGPL), version 3.
|
||||||
Roel Verdult <roel@libnfc.org>
|
Roel Verdult <roel@libnfc.org>
|
||||||
Romuald Conty <romuald@libnfc.org>
|
Romuald Conty <romuald@libnfc.org>
|
||||||
Romain Tartière <romain@blogreen.org>
|
Romain Tartière <romain@blogreen.org>
|
||||||
|
Adam Laurie <adam@algroup.co.uk>
|
||||||
.PP
|
.PP
|
||||||
This manual page was written by Romuald Conty <romuald@libnfc.org>.
|
This manual page was written by Romuald Conty <romuald@libnfc.org>.
|
||||||
It is licensed under the terms of the GNU GPL (version 2 or later).
|
It is licensed under the terms of the GNU GPL (version 2 or later).
|
||||||
|
|
|
@ -214,12 +214,59 @@ authenticate (uint32_t uiBlock)
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
read_card (void)
|
unlock_card()
|
||||||
|
{
|
||||||
|
printf ("Unlocking card\n");
|
||||||
|
|
||||||
|
// Configure the CRC
|
||||||
|
if (!nfc_configure (pnd, NDO_HANDLE_CRC, false)) {
|
||||||
|
nfc_perror (pnd, "nfc_configure");
|
||||||
|
exit (EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
// Use raw send/receive methods
|
||||||
|
if (!nfc_configure (pnd, NDO_EASY_FRAMING, false)) {
|
||||||
|
nfc_perror (pnd, "nfc_configure");
|
||||||
|
exit (EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
iso14443a_crc_append(abtHalt, 2);
|
||||||
|
transmit_bytes (abtHalt, 4);
|
||||||
|
// now send unlock
|
||||||
|
if (!transmit_bits (abtUnlock1, 7)) {
|
||||||
|
printf("unlock failure!\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!transmit_bytes (abtUnlock2, 1)) {
|
||||||
|
printf("unlock failure!\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// reset reader
|
||||||
|
// Configure the CRC
|
||||||
|
if (!nfc_configure (pnd, NDO_HANDLE_CRC, true)) {
|
||||||
|
nfc_perror (pnd, "nfc_configure");
|
||||||
|
exit (EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
// Switch off raw send/receive methods
|
||||||
|
if (!nfc_configure (pnd, NDO_EASY_FRAMING, true)) {
|
||||||
|
nfc_perror (pnd, "nfc_configure");
|
||||||
|
exit (EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
read_card (int read_unlocked)
|
||||||
{
|
{
|
||||||
int32_t iBlock;
|
int32_t iBlock;
|
||||||
bool bFailure = false;
|
bool bFailure = false;
|
||||||
uint32_t uiReadBlocks = 0;
|
uint32_t uiReadBlocks = 0;
|
||||||
|
|
||||||
|
if(read_unlocked)
|
||||||
|
if (!unlock_card())
|
||||||
|
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
|
||||||
|
@ -243,7 +290,7 @@ read_card (void)
|
||||||
fflush (stdout);
|
fflush (stdout);
|
||||||
|
|
||||||
// Try to authenticate for the current sector
|
// Try to authenticate for the current sector
|
||||||
if (!authenticate (iBlock)) {
|
if (!read_unlocked && !authenticate (iBlock)) {
|
||||||
printf ("!\nError: authentication failed for block 0x%02x\n", iBlock);
|
printf ("!\nError: authentication failed for block 0x%02x\n", iBlock);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -286,46 +333,9 @@ write_card (int write_block_zero)
|
||||||
uint32_t uiWriteBlocks = 0;
|
uint32_t uiWriteBlocks = 0;
|
||||||
|
|
||||||
|
|
||||||
if(write_block_zero) {
|
if(write_block_zero)
|
||||||
printf ("Unlocking card\n");
|
if (!unlock_card())
|
||||||
|
|
||||||
// Configure the CRC
|
|
||||||
if (!nfc_configure (pnd, NDO_HANDLE_CRC, false)) {
|
|
||||||
nfc_perror (pnd, "nfc_configure");
|
|
||||||
exit (EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
// Use raw send/receive methods
|
|
||||||
if (!nfc_configure (pnd, NDO_EASY_FRAMING, false)) {
|
|
||||||
nfc_perror (pnd, "nfc_configure");
|
|
||||||
exit (EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
iso14443a_crc_append(abtHalt, 2);
|
|
||||||
transmit_bytes (abtHalt, 4);
|
|
||||||
// now send unlock
|
|
||||||
if (!transmit_bits (abtUnlock1, 7)) {
|
|
||||||
printf("unlock failure!\n");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
if (!transmit_bytes (abtUnlock2, 1)) {
|
|
||||||
printf("unlock failure!\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// reset reader
|
|
||||||
// Configure the CRC
|
|
||||||
if (!nfc_configure (pnd, NDO_HANDLE_CRC, true)) {
|
|
||||||
nfc_perror (pnd, "nfc_configure");
|
|
||||||
exit (EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
// Switch off raw send/receive methods
|
|
||||||
if (!nfc_configure (pnd, NDO_EASY_FRAMING, true)) {
|
|
||||||
nfc_perror (pnd, "nfc_configure");
|
|
||||||
exit (EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
printf ("Writing %d blocks |", uiBlocks + 1);
|
printf ("Writing %d blocks |", uiBlocks + 1);
|
||||||
// Write the card from begin to end;
|
// Write the card from begin to end;
|
||||||
|
@ -418,18 +428,18 @@ typedef enum {
|
||||||
ACTION_READ,
|
ACTION_READ,
|
||||||
ACTION_WRITE,
|
ACTION_WRITE,
|
||||||
ACTION_EXTRACT,
|
ACTION_EXTRACT,
|
||||||
ACTION_USAGE,
|
ACTION_USAGE
|
||||||
ACTION_WRITE_UNLOCKED
|
|
||||||
} action_t;
|
} action_t;
|
||||||
|
|
||||||
static void
|
static void
|
||||||
print_usage (const char *pcProgramName)
|
print_usage (const char *pcProgramName)
|
||||||
{
|
{
|
||||||
printf ("Usage: ");
|
printf ("Usage: ");
|
||||||
printf ("%s r|w|u a|b <dump.mfd> [<keys.mfd>]\n", pcProgramName);
|
printf ("%s r|R|w|W a|b <dump.mfd> [<keys.mfd>]\n", pcProgramName);
|
||||||
printf (" r|w|u - Perform read from (r) or write to (w) or unlocked write to (u) card\n");
|
printf (" r|R|w|W - Perform read from (r) or unlocked read from (R) or write to (w) or unlocked write to (W) card\n");
|
||||||
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 (" *** and only works with special Mifare 1K cards (Chinese clones)\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|b - Use A or B keys for action\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");
|
||||||
|
@ -447,6 +457,7 @@ main (int argc, const char *argv[])
|
||||||
byte_t *pbtUID;
|
byte_t *pbtUID;
|
||||||
FILE *pfKeys = NULL;
|
FILE *pfKeys = NULL;
|
||||||
FILE *pfDump = NULL;
|
FILE *pfDump = NULL;
|
||||||
|
int unlock= 0;
|
||||||
|
|
||||||
if (argc < 2) {
|
if (argc < 2) {
|
||||||
print_usage (argv[0]);
|
print_usage (argv[0]);
|
||||||
|
@ -454,22 +465,24 @@ main (int argc, const char *argv[])
|
||||||
}
|
}
|
||||||
const char *command = argv[1];
|
const char *command = argv[1];
|
||||||
|
|
||||||
if (strcmp (command, "r") == 0) {
|
if (strcmp (command, "r") == 0 || strcmp (command, "R") == 0) {
|
||||||
if (argc < 4) {
|
if (argc < 4) {
|
||||||
print_usage (argv[0]);
|
print_usage (argv[0]);
|
||||||
exit (EXIT_FAILURE);
|
exit (EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
atAction = ACTION_READ;
|
atAction = ACTION_READ;
|
||||||
|
if (strcmp (command, "R") == 0)
|
||||||
|
unlock= 1;
|
||||||
bUseKeyA = tolower ((int) ((unsigned char) *(argv[2]))) == 'a';
|
bUseKeyA = tolower ((int) ((unsigned char) *(argv[2]))) == 'a';
|
||||||
bUseKeyFile = (argc > 4);
|
bUseKeyFile = (argc > 4);
|
||||||
} else if (strcmp (command, "w") == 0 || strcmp (command, "u") == 0) {
|
} else if (strcmp (command, "w") == 0 || strcmp (command, "W") == 0) {
|
||||||
if (argc < 4) {
|
if (argc < 4) {
|
||||||
print_usage (argv[0]);
|
print_usage (argv[0]);
|
||||||
exit (EXIT_FAILURE);
|
exit (EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
atAction = ACTION_WRITE;
|
atAction = ACTION_WRITE;
|
||||||
if (strcmp (command, "u") == 0)
|
if (strcmp (command, "W") == 0)
|
||||||
atAction = ACTION_WRITE_UNLOCKED;
|
unlock= 1;
|
||||||
bUseKeyA = tolower ((int) ((unsigned char) *(argv[2]))) == 'a';
|
bUseKeyA = tolower ((int) ((unsigned char) *(argv[2]))) == 'a';
|
||||||
bUseKeyFile = (argc > 4);
|
bUseKeyFile = (argc > 4);
|
||||||
} else if (strcmp (command, "x") == 0) {
|
} else if (strcmp (command, "x") == 0) {
|
||||||
|
@ -487,7 +500,6 @@ main (int argc, const char *argv[])
|
||||||
break;
|
break;
|
||||||
case ACTION_READ:
|
case ACTION_READ:
|
||||||
case ACTION_WRITE:
|
case ACTION_WRITE:
|
||||||
case ACTION_WRITE_UNLOCKED:
|
|
||||||
if (bUseKeyFile) {
|
if (bUseKeyFile) {
|
||||||
pfKeys = fopen (argv[4], "rb");
|
pfKeys = fopen (argv[4], "rb");
|
||||||
if (pfKeys == NULL) {
|
if (pfKeys == NULL) {
|
||||||
|
@ -580,7 +592,7 @@ main (int argc, const char *argv[])
|
||||||
printf ("Guessing size: seems to be a %i-byte card\n", (uiBlocks + 1) * 16);
|
printf ("Guessing size: seems to be a %i-byte card\n", (uiBlocks + 1) * 16);
|
||||||
|
|
||||||
if (atAction == ACTION_READ) {
|
if (atAction == ACTION_READ) {
|
||||||
if (read_card ()) {
|
if (read_card (unlock)) {
|
||||||
printf ("Writing data to file: %s ...", argv[3]);
|
printf ("Writing data to file: %s ...", argv[3]);
|
||||||
fflush (stdout);
|
fflush (stdout);
|
||||||
pfDump = fopen (argv[3], "wb");
|
pfDump = fopen (argv[3], "wb");
|
||||||
|
@ -596,10 +608,7 @@ main (int argc, const char *argv[])
|
||||||
fclose (pfDump);
|
fclose (pfDump);
|
||||||
}
|
}
|
||||||
} else if (atAction == ACTION_WRITE) {
|
} else if (atAction == ACTION_WRITE) {
|
||||||
write_card (0);
|
write_card (unlock);
|
||||||
}
|
|
||||||
else if (atAction == ACTION_WRITE_UNLOCKED) {
|
|
||||||
write_card (1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nfc_disconnect (pnd);
|
nfc_disconnect (pnd);
|
||||||
|
|
|
@ -3,12 +3,20 @@
|
||||||
nfc-mfsetuid \- MIFARE 1K special card UID setting and recovery tool
|
nfc-mfsetuid \- MIFARE 1K special card UID setting and recovery tool
|
||||||
.SH SYNOPSIS
|
.SH SYNOPSIS
|
||||||
.B nfc-mfsetuid
|
.B nfc-mfsetuid
|
||||||
|
[UID]
|
||||||
|
|
||||||
.SH DESCRIPTION
|
.SH DESCRIPTION
|
||||||
.B nfc-mfsetuid
|
.B nfc-mfsetuid
|
||||||
is a MIFARE tool that allows setting of UID on special versions (Chinese clones) of Mifare 1K cards. It will also recover
|
is a MIFARE tool that allows setting of UID on special versions (Chinese clones) of Mifare 1K cards. It will also recover
|
||||||
damaged cards that have had invalid data written to block 0 (e.g. wrong BCC). Currently only 4 Byte UID is supported.
|
damaged cards that have had invalid data written to block 0 (e.g. wrong BCC). Currently only 4 Byte UID is supported.
|
||||||
|
Specify an eight hex character UID or leave blank for the default '01234567'.
|
||||||
|
|
||||||
|
.SH OPTIONS
|
||||||
|
.B -f
|
||||||
|
Format. Wipe all data (set to 0xFF) and reset ACLs to defaults.
|
||||||
|
|
||||||
|
.B -q
|
||||||
|
Quiet. Suppress output of commands and responses.
|
||||||
.SH BUGS
|
.SH BUGS
|
||||||
Please report any bugs on the
|
Please report any bugs on the
|
||||||
.B libnfc
|
.B libnfc
|
||||||
|
|
|
@ -81,8 +81,10 @@ byte_t abtHalt[4] = { 0x50, 0x00, 0x00, 0x00 };
|
||||||
// special unlock command
|
// special unlock command
|
||||||
byte_t abtUnlock1[1] = { 0x40 };
|
byte_t abtUnlock1[1] = { 0x40 };
|
||||||
byte_t abtUnlock2[1] = { 0x43 };
|
byte_t abtUnlock2[1] = { 0x43 };
|
||||||
|
byte_t abtWipe[1] = { 0x41 };
|
||||||
byte_t abtWrite[4] = { 0xa0, 0x00, 0x5f, 0xb1 };
|
byte_t abtWrite[4] = { 0xa0, 0x00, 0x5f, 0xb1 };
|
||||||
byte_t abtData[18] = { 0x01, 0x23, 0x45, 0x67, 0x00, 0x08, 0x04, 0x00, 0x46, 0x59, 0x25, 0x58, 0x49, 0x10, 0x23, 0x02, 0x23, 0xeb };
|
byte_t abtData[18] = { 0x01, 0x23, 0x45, 0x67, 0x00, 0x08, 0x04, 0x00, 0x46, 0x59, 0x25, 0x58, 0x49, 0x10, 0x23, 0x02, 0x23, 0xeb };
|
||||||
|
byte_t abtBlank[18] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x07, 0x80, 0x69, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x36, 0xCC };
|
||||||
|
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
|
@ -134,7 +136,8 @@ print_usage (char *argv[])
|
||||||
printf ("Usage: %s [OPTIONS] [UID]\n", argv[0]);
|
printf ("Usage: %s [OPTIONS] [UID]\n", argv[0]);
|
||||||
printf ("Options:\n");
|
printf ("Options:\n");
|
||||||
printf ("\t-h\tHelp. Print this message.\n");
|
printf ("\t-h\tHelp. Print this message.\n");
|
||||||
printf ("\t-q\tQuiet mode. Suppress output of READER and EMULATOR data (improves timing).\n");
|
printf ("\t-f\tFormat. Delete all data (set to 0xFF) and reset ACLs to default.\n");
|
||||||
|
printf ("\t-q\tQuiet mode. Suppress output of READER and CARD data (improves timing).\n");
|
||||||
printf ("\n\tSpecify UID (4 HEX bytes) to set UID, or leave blank for default '01234567'.\n");
|
printf ("\n\tSpecify UID (4 HEX bytes) to set UID, or leave blank for default '01234567'.\n");
|
||||||
printf ("\tThis utility can be used to recover cards that have been damaged by writing bad\n");
|
printf ("\tThis utility can be used to recover cards that have been damaged by writing bad\n");
|
||||||
printf ("\tdata (e.g. wrong BCC), thus making them non-selectable by most tools/readers.\n");
|
printf ("\tdata (e.g. wrong BCC), thus making them non-selectable by most tools/readers.\n");
|
||||||
|
@ -145,14 +148,18 @@ int
|
||||||
main (int argc, char *argv[])
|
main (int argc, char *argv[])
|
||||||
{
|
{
|
||||||
int arg, i;
|
int arg, i;
|
||||||
|
bool format= false;
|
||||||
unsigned int c;
|
unsigned int c;
|
||||||
char tmp[3]= { 0x00, 0x00, 0x00 };
|
char tmp[3]= { 0x00, 0x00, 0x00 };
|
||||||
|
|
||||||
|
|
||||||
// Get commandline options
|
// Get commandline options
|
||||||
for (arg = 1; arg < argc; arg++) {
|
for (arg = 1; arg < argc; arg++) {
|
||||||
if (0 == strcmp (argv[arg], "-h")) {
|
if (0 == strcmp (argv[arg], "-h")) {
|
||||||
print_usage (argv);
|
print_usage (argv);
|
||||||
exit(EXIT_SUCCESS);
|
exit(EXIT_SUCCESS);
|
||||||
|
} else if (0 == strcmp (argv[arg], "-f")) {
|
||||||
|
format= true;
|
||||||
} else if (0 == strcmp (argv[arg], "-q")) {
|
} else if (0 == strcmp (argv[arg], "-q")) {
|
||||||
quiet_output = true;
|
quiet_output = true;
|
||||||
} else if (strlen(argv[arg]) == 8) {
|
} else if (strlen(argv[arg]) == 8) {
|
||||||
|
@ -322,9 +329,22 @@ main (int argc, char *argv[])
|
||||||
iso14443a_crc_append(abtHalt, 2);
|
iso14443a_crc_append(abtHalt, 2);
|
||||||
transmit_bytes (abtHalt, 4);
|
transmit_bytes (abtHalt, 4);
|
||||||
transmit_bits (abtUnlock1,7);
|
transmit_bits (abtUnlock1,7);
|
||||||
|
if(format) {
|
||||||
|
transmit_bytes (abtWipe,1);
|
||||||
|
transmit_bytes (abtHalt, 4);
|
||||||
|
transmit_bits (abtUnlock1,7);
|
||||||
|
}
|
||||||
transmit_bytes (abtUnlock2,1);
|
transmit_bytes (abtUnlock2,1);
|
||||||
transmit_bytes (abtWrite,4);
|
transmit_bytes (abtWrite,4);
|
||||||
transmit_bytes (abtData,18);
|
transmit_bytes (abtData,18);
|
||||||
|
if(format) {
|
||||||
|
for(i= 3 ; i < 64 ; i += 4) {
|
||||||
|
abtWrite[1]= (char) i;
|
||||||
|
iso14443a_crc_append (abtWrite, 2);
|
||||||
|
transmit_bytes (abtWrite,4);
|
||||||
|
transmit_bytes (abtBlank,18);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
nfc_disconnect (pnd);
|
nfc_disconnect (pnd);
|
||||||
|
|
Loading…
Reference in a new issue