From e401f4ec4ee220607d339d0cb11c780f9eab0a1e Mon Sep 17 00:00:00 2001 From: Adam Laurie Date: Mon, 5 Sep 2011 14:40:56 +0000 Subject: [PATCH] Write special Mifare 1K cards, including Block 0 / UID --- examples/Makefile.am | 5 + examples/nfc-mfclassic.1 | 10 +- examples/nfc-mfclassic.c | 123 +++++++++++++-- examples/nfc-mfsetuid.c | 331 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 458 insertions(+), 11 deletions(-) create mode 100644 examples/nfc-mfsetuid.c diff --git a/examples/Makefile.am b/examples/Makefile.am index c6d93ed..317d3d0 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -10,6 +10,7 @@ bin_PROGRAMS = \ nfc-emulate-uid \ nfc-list \ nfc-mfclassic \ + nfc-mfsetuid \ nfc-mfultralight \ nfc-poll \ nfc-relay \ @@ -51,6 +52,10 @@ nfc_mfclassic_SOURCES = nfc-mfclassic.c mifare.c mifare.h nfc_mfclassic_LDADD = $(top_builddir)/libnfc/libnfc.la \ libnfcutils.la +nfc_mfsetuid_SOURCES = nfc-mfsetuid.c +nfc_mfsetuid_LDADD = $(top_builddir)/libnfc/libnfc.la \ + libnfcutils.la + nfc_relay_SOURCES = nfc-relay.c nfc_relay_LDADD = $(top_builddir)/libnfc/libnfc.la \ libnfcutils.la diff --git a/examples/nfc-mfclassic.1 b/examples/nfc-mfclassic.1 index 156a3a2..e39c78d 100644 --- a/examples/nfc-mfclassic.1 +++ b/examples/nfc-mfclassic.1 @@ -3,7 +3,7 @@ nfc-mfclassic \- MIFARE Classic command line tool .SH SYNOPSIS .B nfc-mfclassic -.RI \fR\fBr\fR|\fBw\fR +.RI \fR\fBr\fR|\fBw\fR\fR|\fBu\fR .RI \fR\fBa\fR|\fBb\fR .IR DUMP .IR [KEYS] @@ -27,12 +27,18 @@ 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 'u' 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. + .SH OPTIONS -.BR r " | " w +.BR r " | " w " | " u Perform read from ( .B r ) or write to ( .B w +) or unlocked write to ( +.B u ) card. .TP .BR a " | " b diff --git a/examples/nfc-mfclassic.c b/examples/nfc-mfclassic.c index 9b7334e..8d33eca 100644 --- a/examples/nfc-mfclassic.c +++ b/examples/nfc-mfclassic.c @@ -3,6 +3,7 @@ * * Copyright (C) 2009, Roel Verdult * Copyright (C) 2010, Romuald Conty, Romain Tartière + * Copyright (C) 2011, Adam Laurie * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -78,6 +79,53 @@ static const nfc_modulation_t nmMifare = { static size_t num_keys = sizeof (keys) / 6; +#define MAX_FRAME_LEN 264 + +static byte_t abtRx[MAX_FRAME_LEN]; +static size_t szRxBits; +static size_t szRx = sizeof(abtRx); + +byte_t abtHalt[4] = { 0x50, 0x00, 0x00, 0x00 }; + +// special unlock command +byte_t abtUnlock1[1] = { 0x40 }; +byte_t abtUnlock2[1] = { 0x43 }; + +static bool +transmit_bits (const byte_t * pbtTx, const size_t szTxBits) +{ + // Show transmitted command + printf ("Sent bits: "); + print_hex_bits (pbtTx, szTxBits); + // Transmit the bit frame command, we don't use the arbitrary parity feature + if (!nfc_initiator_transceive_bits (pnd, pbtTx, szTxBits, NULL, abtRx, &szRxBits, NULL)) + return false; + + // Show received answer + printf ("Received bits: "); + print_hex_bits (abtRx, szRxBits); + // Succesful transfer + return true; +} + + +static bool +transmit_bytes (const byte_t * pbtTx, const size_t szTx) +{ + // Show transmitted command + printf ("Sent bits: "); + print_hex (pbtTx, szTx); + // Transmit the command bytes + if (!nfc_initiator_transceive_bytes (pnd, pbtTx, szTx, abtRx, &szRx)) + return false; + + // Show received answer + printf ("Received bits: "); + print_hex (abtRx, szRx); + // Succesful transfer + return true; +} + static void print_success_or_failure (bool bFailure, uint32_t * uiBlockCounter) { @@ -231,14 +279,55 @@ read_card (void) } static bool -write_card (void) +write_card (int write_block_zero) { uint32_t uiBlock; bool bFailure = false; uint32_t uiWriteBlocks = 0; - printf ("Writing %d blocks |", uiBlocks + 1); + if(write_block_zero) { + 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); + } + } + + + + printf ("Writing %d blocks |", uiBlocks + 1); // Write the card from begin to end; for (uiBlock = 0; uiBlock <= uiBlocks; uiBlock++) { // Authenticate everytime we reach the first sector of a new block @@ -260,7 +349,7 @@ write_card (void) fflush (stdout); // Try to authenticate for the current sector - if (!authenticate (uiBlock)) { + if (!write_block_zero && !authenticate (uiBlock)) { printf ("!\nError: authentication failed for block %02x\n", uiBlock); return false; } @@ -279,13 +368,21 @@ write_card (void) } } else { // The first block 0x00 is read only, skip this - if (uiBlock == 0) + if (uiBlock == 0 && ! write_block_zero) continue; + // Make sure a earlier write did not fail if (!bFailure) { // Try to write the data block memcpy (mp.mpd.abtData, mtDump.amb[uiBlock].mbd.abtData, 16); + // do not write a block 0 with incorrect BCC - card will be made invalid! + if (uiBlock == 0) { + if((mp.mpd.abtData[0] ^ mp.mpd.abtData[1] ^ mp.mpd.abtData[2] ^ mp.mpd.abtData[3] ^ mp.mpd.abtData[4]) != 0x00) { + printf ("!\nError: incorrect BCC in MFD file!\n"); + return false; + } + } if (!nfc_initiator_mifare_cmd (pnd, MC_WRITE, uiBlock, &mp)) bFailure = true; } @@ -321,15 +418,17 @@ typedef enum { ACTION_READ, ACTION_WRITE, ACTION_EXTRACT, - ACTION_USAGE + ACTION_USAGE, + ACTION_WRITE_UNLOCKED } action_t; static void print_usage (const char *pcProgramName) { printf ("Usage: "); - printf ("%s r|w a|b []\n", pcProgramName); - printf (" r|w - Perform read from (r) or write to (w) card\n"); + printf ("%s r|w|u a|b []\n", pcProgramName); + printf (" r|w|u - Perform read from (r) or write to (w) or unlocked write to (u) card\n"); + printf (" *** note that unlocked write will attempt to overwrite block 0 including UID\n"); printf (" a|b - Use A or B keys for action\n"); printf (" - MiFare Dump (MFD) used to write (card to MFD) or (MFD to card)\n"); printf (" - MiFare Dump (MFD) that contain the keys (optional)\n"); @@ -362,12 +461,14 @@ main (int argc, const char *argv[]) atAction = ACTION_READ; bUseKeyA = tolower ((int) ((unsigned char) *(argv[2]))) == 'a'; bUseKeyFile = (argc > 4); - } else if (strcmp (command, "w") == 0) { + } else if (strcmp (command, "w") == 0 || strcmp (command, "u") == 0) { if (argc < 4) { print_usage (argv[0]); exit (EXIT_FAILURE); } atAction = ACTION_WRITE; + if (strcmp (command, "u") == 0) + atAction = ACTION_WRITE_UNLOCKED; bUseKeyA = tolower ((int) ((unsigned char) *(argv[2]))) == 'a'; bUseKeyFile = (argc > 4); } else if (strcmp (command, "x") == 0) { @@ -385,6 +486,7 @@ main (int argc, const char *argv[]) break; case ACTION_READ: case ACTION_WRITE: + case ACTION_WRITE_UNLOCKED: if (bUseKeyFile) { pfKeys = fopen (argv[4], "rb"); if (pfKeys == NULL) { @@ -493,7 +595,10 @@ main (int argc, const char *argv[]) fclose (pfDump); } } else if (atAction == ACTION_WRITE) { - write_card (); + write_card (0); + } + else if (atAction == ACTION_WRITE_UNLOCKED) { + write_card (1); } nfc_disconnect (pnd); diff --git a/examples/nfc-mfsetuid.c b/examples/nfc-mfsetuid.c new file mode 100644 index 0000000..580c070 --- /dev/null +++ b/examples/nfc-mfsetuid.c @@ -0,0 +1,331 @@ +/*- + * Public platform independent Near Field Communication (NFC) library examples + * + * Copyright (C) 2009, Roel Verdult + * Copyright (C) 2011, Adam Laurie + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2 )Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * Note that this license only applies on the examples, NFC library itself is under LGPL + * + */ + +/** + * @file nfc-mfsetuid.c + * @brief Set UID of special Mifare cards + */ + +/** + * based on nfc-anticol.c + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif // HAVE_CONFIG_H + +#include +#include +#include +#include +#include +#include + +#include + +#include "nfc-utils.h" + +#define SAK_FLAG_ATS_SUPPORTED 0x20 + +#define MAX_FRAME_LEN 264 + +static byte_t abtRx[MAX_FRAME_LEN]; +static size_t szRxBits; +static size_t szRx = sizeof(abtRx); +static byte_t abtRawUid[12]; +static byte_t abtAtqa[2]; +static byte_t abtSak; +static byte_t abtAts[MAX_FRAME_LEN]; +static byte_t szAts = 0; +static size_t szCL = 1;//Always start with Cascade Level 1 (CL1) +static nfc_device_t *pnd; + +bool quiet_output = false; +bool iso_ats_supported = false; + +// ISO14443A Anti-Collision Commands +byte_t abtReqa[1] = { 0x26 }; +byte_t abtSelectAll[2] = { 0x93, 0x20 }; +byte_t abtSelectTag[9] = { 0x93, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +byte_t abtRats[4] = { 0xe0, 0x50, 0x00, 0x00 }; +byte_t abtHalt[4] = { 0x50, 0x00, 0x00, 0x00 }; +#define CASCADE_BIT 0x04 + +// special unlock command +byte_t abtUnlock1[1] = { 0x40 }; +byte_t abtUnlock2[1] = { 0x43 }; +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 }; + + +static bool +transmit_bits (const byte_t * pbtTx, const size_t szTxBits) +{ + // Show transmitted command + if (!quiet_output) { + printf ("Sent bits: "); + print_hex_bits (pbtTx, szTxBits); + } + // Transmit the bit frame command, we don't use the arbitrary parity feature + if (!nfc_initiator_transceive_bits (pnd, pbtTx, szTxBits, NULL, abtRx, &szRxBits, NULL)) + return false; + + // Show received answer + if (!quiet_output) { + printf ("Received bits: "); + print_hex_bits (abtRx, szRxBits); + } + // Succesful transfer + return true; +} + + +static bool +transmit_bytes (const byte_t * pbtTx, const size_t szTx) +{ + // Show transmitted command + if (!quiet_output) { + printf ("Sent bits: "); + print_hex (pbtTx, szTx); + } + // Transmit the command bytes + if (!nfc_initiator_transceive_bytes (pnd, pbtTx, szTx, abtRx, &szRx)) + return false; + + // Show received answer + if (!quiet_output) { + printf ("Received bits: "); + print_hex (abtRx, szRx); + } + // Succesful transfer + return true; +} + +static void +print_usage (char *argv[]) +{ + printf ("Usage: %s [OPTIONS] [UID]\n", argv[0]); + printf ("Options:\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 ("\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 ("\tdata (e.g. wrong BCC), thus making them non-selectable by most tools/readers.\n\n"); +} + +int +main (int argc, char *argv[]) +{ + int arg, i; + unsigned int c; + char tmp[3]= { 0x00, 0x00, 0x00 }; + + // Get commandline options + for (arg = 1; arg < argc; arg++) { + if (0 == strcmp (argv[arg], "-h")) { + print_usage (argv); + exit(EXIT_SUCCESS); + } else if (0 == strcmp (argv[arg], "-q")) { + quiet_output = true; + } else if (strlen(argv[arg]) == 8) { + for(i= 0 ; i < 4 ; ++i) { + memcpy(tmp, argv[arg]+i*2, 2); + sscanf(tmp, "%02x", &c); + abtData[i]= (char) c; + } + abtData[4]= abtData[0] ^ abtData[1] ^ abtData[2] ^ abtData[3]; + iso14443a_crc_append (abtData, 16); + } else { + ERR ("%s is not supported option.", argv[arg]); + print_usage (argv); + exit(EXIT_FAILURE); + } + } + + // Try to open the NFC reader + pnd = nfc_connect (NULL); + + if (!pnd) { + printf ("Error connecting NFC reader\n"); + exit(EXIT_FAILURE); + } + + // Initialise NFC device as "initiator" + nfc_initiator_init (pnd); + + // 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); + } + // Disable 14443-4 autoswitching + if (!nfc_configure (pnd, NDO_AUTO_ISO14443_4, false)) { + nfc_perror (pnd, "nfc_configure"); + exit (EXIT_FAILURE); + } + + printf ("Connected to NFC reader: %s\n\n", pnd->acName); + + // Send the 7 bits request command specified in ISO 14443A (0x26) + if (!transmit_bits (abtReqa, 7)) { + printf ("Error: No tag available\n"); + nfc_disconnect (pnd); + return 1; + } + memcpy (abtAtqa, abtRx, 2); + + // Anti-collision + transmit_bytes (abtSelectAll, 2); + + // Check answer + if ((abtRx[0] ^ abtRx[1] ^ abtRx[2] ^ abtRx[3] ^ abtRx[4]) != 0) { + printf("WARNING: BCC check failed!\n"); + } + + // Save the UID CL1 + memcpy (abtRawUid, abtRx, 4); + + //Prepare and send CL1 Select-Command + memcpy (abtSelectTag + 2, abtRx, 5); + iso14443a_crc_append (abtSelectTag, 7); + transmit_bytes (abtSelectTag, 9); + abtSak = abtRx[0]; + + // Test if we are dealing with a CL2 + if (abtSak & CASCADE_BIT) { + szCL = 2;//or more + // Check answer + if (abtRawUid[0] != 0x88) { + printf("WARNING: Cascade bit set but CT != 0x88!\n"); + } + } + + if(szCL == 2) { + // We have to do the anti-collision for cascade level 2 + + // Prepare CL2 commands + abtSelectAll[0] = 0x95; + + // Anti-collision + transmit_bytes (abtSelectAll, 2); + + // Check answer + if ((abtRx[0] ^ abtRx[1] ^ abtRx[2] ^ abtRx[3] ^ abtRx[4]) != 0) { + printf("WARNING: BCC check failed!\n"); + } + + // Save UID CL2 + memcpy (abtRawUid + 4, abtRx, 4); + + // Selection + abtSelectTag[0] = 0x95; + memcpy (abtSelectTag + 2, abtRx, 5); + iso14443a_crc_append (abtSelectTag, 7); + transmit_bytes (abtSelectTag, 9); + abtSak = abtRx[0]; + + // Test if we are dealing with a CL3 + if (abtSak & CASCADE_BIT) { + szCL = 3; + // Check answer + if (abtRawUid[0] != 0x88) { + printf("WARNING: Cascade bit set but CT != 0x88!\n"); + } + } + + if ( szCL == 3) { + // We have to do the anti-collision for cascade level 3 + + // Prepare and send CL3 AC-Command + abtSelectAll[0] = 0x97; + transmit_bytes (abtSelectAll, 2); + + // Check answer + if ((abtRx[0] ^ abtRx[1] ^ abtRx[2] ^ abtRx[3] ^ abtRx[4]) != 0) { + printf("WARNING: BCC check failed!\n"); + } + + // Save UID CL3 + memcpy (abtRawUid + 8, abtRx, 4); + + // Prepare and send final Select-Command + abtSelectTag[0] = 0x97; + memcpy (abtSelectTag + 2, abtRx, 5); + iso14443a_crc_append (abtSelectTag, 7); + transmit_bytes (abtSelectTag, 9); + abtSak = abtRx[0]; + } + } + + // Request ATS, this only applies to tags that support ISO 14443A-4 + if (abtRx[0] & SAK_FLAG_ATS_SUPPORTED) { + iso_ats_supported = true; + } + + printf ("\nFound tag with\n UID: "); + switch (szCL) { + case 1: + printf ("%02x%02x%02x%02x", abtRawUid[0], abtRawUid[1], abtRawUid[2], abtRawUid[3]); + break; + case 2: + printf ("%02x%02x%02x", abtRawUid[1], abtRawUid[2], abtRawUid[3]); + printf ("%02x%02x%02x%02x", abtRawUid[4], abtRawUid[5], abtRawUid[6], abtRawUid[7]); + break; + case 3: + printf ("%02x%02x%02x", abtRawUid[1], abtRawUid[2], abtRawUid[3]); + printf ("%02x%02x%02x", abtRawUid[5], abtRawUid[6], abtRawUid[7]); + printf ("%02x%02x%02x%02x", abtRawUid[8], abtRawUid[9], abtRawUid[10], abtRawUid[11]); + break; + } + printf("\n"); + printf("ATQA: %02x%02x\n SAK: %02x\n", abtAtqa[1], abtAtqa[0], abtSak); + if (szAts > 1) { // if = 1, it's not actual ATS but error code + printf(" ATS: "); + print_hex (abtAts, szAts); + } + printf("\n"); + + // now reset UID + iso14443a_crc_append(abtHalt, 2); + transmit_bytes (abtHalt, 4); + transmit_bits (abtUnlock1,7); + transmit_bytes (abtUnlock2,1); + transmit_bytes (abtWrite,4); + transmit_bytes (abtData,18); + + + nfc_disconnect (pnd); + return 0; +}