Assembling a dongle to work with contactless cards

Father

Professional
Messages
2,601
Reputation
4
Reaction score
633
Points
113
Putting together a dongle to work with contactless cards

Introduction
Contactless cards have long been an integral part of our life. With their help we get to work, use public transport, use them as keys. In this article, I propose to consider in detail both the theoretical foundations of the operation of contactless cards, and in practice to assemble a device for reading and writing data to cards. As a hardware platform, we need an Arduino Nano and a module for reading firmware, as well as a miniUSB-microUSB / Type C adapter for interacting with a smartphone.
The article will focus on low-level work with cards in the 13.56 MHz range at the byte level. Therefore, I will not touch on NFC technology and the functionality of smartphones for working with cards via NFC using applications such as NFC ReTag.
The solutions and technologies described in the article can be useful both for system administrators and security personnel responsible for access control at the facility, and for information security specialists performing penetration testing.
However, as usual, I remind you that all the information presented in the article is provided for informational purposes only. For the same reason, I deliberately omit the implementation of some of the functions in the described device.

Theory of contactless cards
In this section, we will talk about what cards are. Contactless cards come in two ranges of 125 kHz and 13.56 MHz (we will not consider more exotic ranges) and operate at a distance of 0 to 15 cm.The principle of operation is based on the use of an oscillating circuit, the basic components of which are a microcircuit, a capacitor and an inductor.
The reader emits a magnetic field using an inductor, thereby transmitting current and charging a capacitor that powers the microcircuit. The exchange of information between the card and the reader is carried out through the same coil by modulating the oscillations of the device's electromagnetic field. In the simplest case, the card cyclically continuously transmits only its unique number. However, as we will see later, more complex exchange of information on the basis of a request-response principle is also possible. Many cards have a small flash memory of the order of 1 KB, into which data can be written.
Further, we will talk only about 13.56 MHz, since cards in the 125 KHz range are considered outdated (although they are actively used in Russia) and unsafe. However, if during the penetration test you need to check the security of the access control system based on maps of this range, then you can use this ready-made copier:

A contactless card with an operating range of 13.56 MHz is essentially one kilobyte of non-volatile memory. It is divided into 16 sectors of 4 16-byte blocks each. A block is the smallest addressable unit when working with a map. Sector is a unit with which individual access rights and keys for operations are mapped. Each sector stores its own pair of keys, and the access rights indicate which access is possible with which key. To interact with the card, the PICC protocol is used, which allows receiving data from the card.
The PICC activation algorithm in accordance with the ISO 14443 standard is shown.

As a result of PICC activation, we will receive approximately the following data:
Code:
UID: 0123456789abcdefaa55
Card UID: 01 23 45 67 89 AB CD EF AA 55
Card SAK: 20
PICC type: PICC compliant with ISO / IEC 14443-4
Dumping memory contents not implemented for that PICC type.

PCD_TransceiveData status = 3
000000000000000000000000000000000000000000000000000000000000

Since, when writing the code, we will use a library that already contains the implementation of all the operations necessary for the operation of PICC, and I see no reason to consider in more detail the process of activation and interaction using the PICC protocol. Instead, I suggest moving on to the practical part. But those wishing to get acquainted with the boring theory are invited to read the description of the protocol 14443A (GOST R ISO / IEC 14443-3-2014 Identification cards. Contactless cards on integrated circuits. Close-range cards.)

Concept
We are accustomed to the fact that any device should be self-sufficient. A charged phone, tablet, laptop, all of these devices are self-sufficient and, once charged, do not require any additional hardware components to operate. Another thing is home-made devices, sharpened for specific tasks. To interact with the user, they need buttons, switches, a display (at least an LCD), a power supply. All this complicates the design, (it is necessary to use more parts, solder more contacts) creates more points of failure (more parts and contacts may fail), increases the dimensions and weight of the device (for a portable device, dimensions matter),
Now let's remember that any of us always have a smartphone with us, which can provide all the necessary resources (interaction interface, power) for our device.
Wikipedia interprets the word dongle as a device that, when connected to a port of another device, provides it with additional functionality. Let's take a closer look at the concept of devices that connect to smartphones. This concept greatly simplifies assembly and increases reliability, without requiring serious resources from a smartphone in return. It is enough just to connect the dongle through the USB-microUSB or type-C adapter (depending on the smartphone) and control it using the USB-Serial client. In my subsequent articles, I plan to use dongles more than once for various tasks.

Let's start assembling
We will clearly formulate the requirements and proceed with the assembly. So, our device should connect via microUSB / type-C to the phone. In the USB-Serial application, we connect to the correct port. Next, a menu should open in which we can select the desired mode.

It is proposed to implement the following modes:
  1. Reading the contents of the map
  2. Reading personal data from the card
  3. Writing personal data to the card
The difference between the first and the second points is that in the first case we read all the data from the card (we make a dump), while in the second we only read the values of the fields in which the cardholder's data is written, usually the first and last name.
This is what the choice of the first and second points will be given for the same card:

This functionality is quite enough for assembling a demo device. Now, let's talk about what components we need. Here's all you need:
  1. Arduino Nano (with unsoldered connectors) - https://aliexpress.ru/item/32341832857.html
  2. MFRC-522 module - https://aliexpress.ru/item/32946922921.html
  3. MiniUSB-> microUSB / Typec-C adapter
The Arduino Nano will act as the core of the device. I highly discourage using more powerful Arduins for several reasons. First of all, the Nano is quite enough for our tasks, since we are making a portable device and the size is of great importance, and the Nano is quite small. Also, the Arduino Nano can be purchased with unsoldered pins. In ready-to-use devices, I never use solderless connections, as their reliability in combat conditions tends to zero. Solderless boards are only good for rapid prototyping. For real devices, only a soldering iron. Do not be too lazy to learn how to properly solder - the skill in the household will come in handy.

You need to solder a module to the Arduino to work with contactless cards according to the following diagram.
1581605991935.png


Next, we need to write the firmware for our dongle. We will use the Arduino IDE as a development tool. For those who are not familiar with Arduino at all, I recommend searching the Internet for articles on working with this breadboard, since learning the basics is beyond the scope of this article.
If you have not worked with RFID before, the required libraries are most likely not installed. Therefore, before writing the code, we will install the necessary libraries. To do this, you need to download the file https://github.com/miguelbalboa/rfid/archive/master.zip and unpack the contents of the archive into the% Arduino_IDE% / libraries folder. Now, after starting the Arduino IDE, we should have several sketches with examples of working with the card in the Files -> Examples -> MFRC522 section.

Now let's go directly to coding. First of all, we will implement a menu for communication via the Serial port. To do this, in the standard loop procedure, display the menu options and wait for user input.
Code:
C:
Serial.println("1. Read dump the card");
Serial.println("2. Read personal data");
Serial.println("3. Write personal data");

Depending on the items selected by the user, we launch the appropriate procedures. The algorithms for working with maps are based on the examples included in the MFRC522 library, therefore, in case of problems, you can check the work of individual procedures directly in the examples.
Here I will give the source code of the procedures from my code.

Reading the contents of the card
First of all, we need to make sure that the card is present and can be read from it. To do this, use the values of PICC_IsNewCardPresent () and PICC_ReadCardSerial (), if at least one of them is undefined, exit. If everything is fine, we output the contents of the card to the serial port by its UID.
I want to note that reading data from the card and displaying it on the screen will take a few seconds, so during testing, do not rush to remove the card from the reader.

The procedure for reading a dump from the card will look like this.
Code:
C:
void DumpInfo() {
delay(pause);
if ( ! mfrc522.PICC_IsNewCardPresent()) {
return;
}
if ( ! mfrc522.PICC_ReadCardSerial()) {
return;
}
mfrc522.PICC_DumpToSerial(&(mfrc522.uid));
mfrc522.PICC_HaltA();
}

Reading personal information
Unlike reading a card dump, when reading personal information, we will have to parse the data that we read. In this case, personal information will mean the name (First Name) and surname (Last Name) of the cardholder. Having checked the availability of the card by analogy with the previous example, we read the dump and begin to parse it. To read the dump, we first need to find a key to decrypt the data. In most cases, nobody changes the factory keys, so we use the eight keys specified in the key array. We will place the extracted First Name and Last Name of the cardholder in the arrays buffer1 and buffer2, 18 bytes long.
By the way, you can find many more factory keys using these links. ikarus23 / MifareClassicTool Pay attention to the last sections of the list. I wonder what kind of cards for public toilets are we talking about?

Here is the source code for the procedure for reading the cardholder's personal information.
Code:
C:
void Read_card() {
delay(pause);
// put your main code here, to run repeatedly:
Serial.print(F("Name: "));
byte buffer1[18];
MFRC522::MIFARE_Key key;
for (byte i = 0; i < 6; i++) key.keyByte[i] = 0xFF;
byte block;
byte len;
MFRC522::StatusCode status;
block = 4;
len = 18;
if ( ! mfrc522.PICC_IsNewCardPresent()) {
return;
}
if ( ! mfrc522.PICC_ReadCardSerial()) {
return;
}
Serial.println(F("**Card Detected:**"));
//-------------------------------------------
mfrc522.PICC_DumpDetailsToSerial(&(mfrc522.uid)); //dump some details about the card
//------------------------------------------- GET FIRST NAME
status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, 4, &key, &(mfrc522.uid));
if (status != MFRC522::STATUS_OK) {
Serial.print(F("Authentication failed: "));
Serial.println(mfrc522.GetStatusCodeName(status));
return;
}
status = mfrc522.MIFARE_Read(block, buffer1, &len);
if (status != MFRC522::STATUS_OK) {
Serial.print(F("Reading failed: "));
Serial.println(mfrc522.GetStatusCodeName(status));
return;
}
//PRINT FIRST NAME
for (uint8_t i = 0; i < 16; i++)
{
if (buffer1[i] != 32)
{
Serial.write(buffer1[i]);
}
}
Serial.print(" ");
//---------------------------------------- GET LAST NAME
byte buffer2[18];
block = 1;
status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, 1, &key, &(mfrc522.uid)); //line 834
if (status != MFRC522::STATUS_OK) {
Serial.print(F("Authentication failed: "));
Serial.println(mfrc522.GetStatusCodeName(status));
return;
}
status = mfrc522.MIFARE_Read(block, buffer2, &len);
if (status != MFRC522::STATUS_OK) {
Serial.print(F("Reading failed: "));
Serial.println(mfrc522.GetStatusCodeName(status));
return;
}
//PRINT LAST NAME
for (uint8_t i = 0; i < 16; i++) {
Serial.write(buffer2[i] );
}
//----------------------------------------
Serial.println(F("\n**End Reading**\n"));
delay(1000); //change value if you want to read cards faster
mfrc522.PICC_HaltA();
mfrc522.PCD_StopCrypto1();
}

Recording to the card
Now let's consider the recording of personal information on the card. Here we will receive the string entered by the user into the buf array. We will give 20 seconds to enter this information. Note that in practice, this time may not be enough for typing on a smartphone, so it may be worth increasing the value. After entering First or Last Name, be sure to include the # symbol.
Code:
C:
void Write_card() {
MFRC522::MIFARE_Key key;
for (byte i = 0; i < 6; i++) key.keyByte[i] = 0xFF;
Serial.print(F("Card UID:")); //Dump UID
for (byte i = 0; i < mfrc522.uid.size; i++) {
Serial.print(mfrc522.uid.uidByte[i] < 0x10 ? " 0" : " ");
Serial.print(mfrc522.uid.uidByte[i], HEX);
}
Serial.print(F(" PICC type: ")); // Dump PICC type
MFRC522::PICC_Type piccType = mfrc522.PICC_GetType(mfrc522.uid.sak);
Serial.println(mfrc522.PICC_GetTypeName(piccType));
byte buffer[34];
byte block;
MFRC522::StatusCode status;
byte len;
Serial.setTimeout(20000L) ; // wait until 20 seconds for input from serial
// Ask personal data: Family name
Serial.println(F("Type Family name, ending with #"));
len = Serial.readBytesUntil('#', (char *) buffer, 30) ; // read family name from serial
for (byte i = len; i < 30; i++) buffer[i] = ' '; // pad with spaces
block = 1;
status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, block, &key, &(mfrc522.uid));
if (status != MFRC522::STATUS_OK) {
Serial.print(F("PCD_Authenticate() failed: "));
Serial.println(mfrc522.GetStatusCodeName(status));
return;
}
else Serial.println(F("PCD_Authenticate() success: "));
// Write block
status = mfrc522.MIFARE_Write(block, buffer, 16);
if (status != MFRC522::STATUS_OK) {
Serial.print(F("MIFARE_Write() failed: "));
Serial.println(mfrc522.GetStatusCodeName(status));
return;
}
else Serial.println(F("MIFARE_Write() success: "));
block = 2;
status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, block, &key, &(mfrc522.uid));
if (status != MFRC522::STATUS_OK) {
Serial.print(F("PCD_Authenticate() failed: "));
Serial.println(mfrc522.GetStatusCodeName(status));
return;
}
// Write block
status = mfrc522.MIFARE_Write(block, &buffer[16], 16);
if (status != MFRC522::STATUS_OK) {
Serial.print(F("MIFARE_Write() failed: "));
Serial.println(mfrc522.GetStatusCodeName(status));
return;
}
else Serial.println(F("MIFARE_Write() success: "));
// Ask personal data: First name
Serial.println(F("Type First name, ending with #"));
len = Serial.readBytesUntil('#', (char *) buffer, 20) ; // read first name from serial
for (byte i = len; i < 20; i++) buffer[i] = ' '; // pad with spaces
block = 4;
status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, block, &key, &(mfrc522.uid));
if (status != MFRC522::STATUS_OK) {
Serial.print(F("PCD_Authenticate() failed: "));
Serial.println(mfrc522.GetStatusCodeName(status));
return;
}
// Write block
status = mfrc522.MIFARE_Write(block, buffer, 16);
if (status != MFRC522::STATUS_OK) {
Serial.print(F("MIFARE_Write() failed: "));
Serial.println(mfrc522.GetStatusCodeName(status));
return;
}
else Serial.println(F("MIFARE_Write() success: "));
block = 5;
status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, block, &key, &(mfrc522.uid));
if (status != MFRC522::STATUS_OK) {
Serial.print(F("PCD_Authenticate() failed: "));
Serial.println(mfrc522.GetStatusCodeName(status));
return;
}
// Write block
status = mfrc522.MIFARE_Write(block, &buffer[16], 16);
if (status != MFRC522::STATUS_OK) {
Serial.print(F("MIFARE_Write() failed: "));
Serial.println(mfrc522.GetStatusCodeName(status));
return;
}
else Serial.println(F("MIFARE_Write() success: "));
Serial.println(" ");
mfrc522.PICC_HaltA(); // Halt PICC
mfrc522.PCD_StopCrypto1(); // Stop encryption on PCD
}
We will place the handler of the menu options selected by the user in a separate Start () procedure. There is nothing complicated here, we just wait for user input and, if there is a match, we call the required procedure.

Code:
C:
void start(){
choice = Serial.read();
if(choice == '1')
{
Serial.println("Read dump the card");
DumpInfo();
}
else if(choice == '2')
{
Serial.println("Read personal data");
Read_card();
}
else if(choice == '3')
{
Serial.println("Write personal data");
Write_card();
}
}

As a result, the source code of the entire firmware looks like this:
Code:
C:
#include <SPI.h>
#include <MFRC522.h>
#define RST_PIN         9          // Configurable, see typical pin layout above
#define SS_PIN          10         // Configurable, see typical pin layout above
#define NR_KNOWN_KEYS   8
MFRC522 mfrc522(SS_PIN, RST_PIN);  // Create MFRC522 instance
// Known keys, see: https://code.google.com/p/mfcuk/wiki/MifareClassicDefaultKeys
byte knownKeys[NR_KNOWN_KEYS][MFRC522::MF_KEY_SIZE] =  {
{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, // FF FF FF FF FF FF = factory default
{0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5}, // A0 A1 A2 A3 A4 A5
{0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5}, // B0 B1 B2 B3 B4 B5
{0x4d, 0x3a, 0x99, 0xc3, 0x51, 0xdd}, // 4D 3A 99 C3 51 DD
{0x1a, 0x98, 0x2c, 0x7e, 0x45, 0x9a}, // 1A 98 2C 7E 45 9A
{0xd3, 0xf7, 0xd3, 0xf7, 0xd3, 0xf7}, // D3 F7 D3 F7 D3 F7
{0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}, // AA BB CC DD EE FF
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00}  // 00 00 00 00 00 00
};
char choice;
int pause;
byte buffer[18];
byte block;
byte waarde[64][16];
MFRC522::StatusCode status;
byte newUid[4] {0xDE, 0xAD, 0xBE, 0xFF};
void setup() {
// put your setup code here, to run once:
Serial.begin(9600);   // Initialize serial communications with the PC
while (!Serial);    // Do nothing if no serial port is opened (added for Arduinos based on ATMEGA32U4)
SPI.begin();      // Init SPI bus
mfrc522.PCD_Init();   // Init MFRC522
Serial.println("1. Read dump the card");
Serial.println("2. Read personal data");
Serial.println("3. Write personal data");
pause=2000;
}
void loop() {
// put your main code here, to run repeatedly:
start();
}
void DumpInfo() {
// put your main code here, to run repeatedly:
delay(pause);
if ( ! mfrc522.PICC_IsNewCardPresent()) {
return;
}
if ( ! mfrc522.PICC_ReadCardSerial()) {
return;
}
mfrc522.PICC_DumpToSerial(&(mfrc522.uid));
mfrc522.PICC_HaltA();
}
void Read_card() {
delay(pause);
// put your main code here, to run repeatedly:
Serial.print(F("Name: "));
byte buffer1[18];
MFRC522::MIFARE_Key key;
for (byte i = 0; i < 6; i++) key.keyByte[i] = 0xFF;
//some variables we need
byte block;
byte len;
MFRC522::StatusCode status;
block = 4;
len = 18;
// Look for new cards
if ( ! mfrc522.PICC_IsNewCardPresent()) {
return;
}
// Select one of the cards
if ( ! mfrc522.PICC_ReadCardSerial()) {
return;
}
Serial.println(F("**Card Detected:**"));
mfrc522.PICC_DumpDetailsToSerial(&(mfrc522.uid)); //dump some details about the card
//------------------------------------------- GET FIRST NAME
status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, 4, &key, &(mfrc522.uid)); //line 834 of MFRC522.cpp file
if (status != MFRC522::STATUS_OK) {
Serial.print(F("Authentication failed: "));
Serial.println(mfrc522.GetStatusCodeName(status));
return;
}
status = mfrc522.MIFARE_Read(block, buffer1, &len);
if (status != MFRC522::STATUS_OK) {
Serial.print(F("Reading failed: "));
Serial.println(mfrc522.GetStatusCodeName(status));
return;
}
//PRINT FIRST NAME
for (uint8_t i = 0; i < 16; i++)
{
if (buffer1[i] != 32)
{
Serial.write(buffer1[i]);
}
}
Serial.print(" ");
//---------------------------------------- GET LAST NAME
byte buffer2[18];
block = 1;
status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, 1, &key, &(mfrc522.uid));
if (status != MFRC522::STATUS_OK) {
Serial.print(F("Authentication failed: "));
Serial.println(mfrc522.GetStatusCodeName(status));
return;
}
status = mfrc522.MIFARE_Read(block, buffer2, &len);
if (status != MFRC522::STATUS_OK) {
Serial.print(F("Reading failed: "));
Serial.println(mfrc522.GetStatusCodeName(status));
return;
}
//PRINT LAST NAME
for (uint8_t i = 0; i < 16; i++) {
Serial.write(buffer2[i] );
}
//----------------------------------------
Serial.println(F("\n**End Reading**\n"));
delay(1000); //change value if you want to read cards faster
mfrc522.PICC_HaltA();
mfrc522.PCD_StopCrypto1();
}
void Write_card() {
// put your main code here, to run repeatedly:
MFRC522::MIFARE_Key key;
for (byte i = 0; i < 6; i++) key.keyByte[i] = 0xFF;
// Look for new cards
if ( ! mfrc522.PICC_IsNewCardPresent()) {
return;
}
// Select one of the cards
if ( ! mfrc522.PICC_ReadCardSerial()) {
return;
}
Serial.print(F("Card UID:"));    //Dump UID
for (byte i = 0; i < mfrc522.uid.size; i++) {
Serial.print(mfrc522.uid.uidByte[i] < 0x10 ? " 0" : " ");
Serial.print(mfrc522.uid.uidByte[i], HEX);
}
Serial.print(F(" PICC type: "));   // Dump PICC type
MFRC522::PICC_Type piccType = mfrc522.PICC_GetType(mfrc522.uid.sak);
Serial.println(mfrc522.PICC_GetTypeName(piccType));
byte buffer[34];
byte block;
MFRC522::StatusCode status;
byte len;
Serial.setTimeout(20000L) ;     // wait until 20 seconds for input from serial
// Ask personal data: Family name
Serial.println(F("Type Family name, ending with #"));
len = Serial.readBytesUntil('#', (char *) buffer, 30) ; // read family name from serial
for (byte i = len; i < 30; i++) buffer[i] = ' ';     // pad with spaces
block = 1;
status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, block, &key, &(mfrc522.uid));
if (status != MFRC522::STATUS_OK) {
Serial.print(F("PCD_Authenticate() failed: "));
Serial.println(mfrc522.GetStatusCodeName(status));
return;
}
else Serial.println(F("PCD_Authenticate() success: "));
// Write block
status = mfrc522.MIFARE_Write(block, buffer, 16);
if (status != MFRC522::STATUS_OK) {
Serial.print(F("MIFARE_Write() failed: "));
Serial.println(mfrc522.GetStatusCodeName(status));
return;
}
else Serial.println(F("MIFARE_Write() success: "));
block = 2;
status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, block, &key, &(mfrc522.uid));
if (status != MFRC522::STATUS_OK) {
Serial.print(F("PCD_Authenticate() failed: "));
Serial.println(mfrc522.GetStatusCodeName(status));
return;
}
// Write block
status = mfrc522.MIFARE_Write(block, &buffer[16], 16);
if (status != MFRC522::STATUS_OK) {
Serial.print(F("MIFARE_Write() failed: "));
Serial.println(mfrc522.GetStatusCodeName(status));
return;
}
else Serial.println(F("MIFARE_Write() success: "));
// Ask personal data: First name
Serial.println(F("Type First name, ending with #"));
len = Serial.readBytesUntil('#', (char *) buffer, 20) ; // read first name from serial
for (byte i = len; i < 20; i++) buffer[i] = ' ';     // pad with spaces
block = 4;
status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, block, &key, &(mfrc522.uid));
if (status != MFRC522::STATUS_OK) {
Serial.print(F("PCD_Authenticate() failed: "));
Serial.println(mfrc522.GetStatusCodeName(status));
return;
}
// Write block
status = mfrc522.MIFARE_Write(block, buffer, 16);
if (status != MFRC522::STATUS_OK) {
Serial.print(F("MIFARE_Write() failed: "));
Serial.println(mfrc522.GetStatusCodeName(status));
return;
}
else Serial.println(F("MIFARE_Write() success: "));
block = 5;
status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, block, &key, &(mfrc522.uid));
if (status != MFRC522::STATUS_OK) {
Serial.print(F("PCD_Authenticate() failed: "));
Serial.println(mfrc522.GetStatusCodeName(status));
return;
}
// Write block
status = mfrc522.MIFARE_Write(block, &buffer[16], 16);
if (status != MFRC522::STATUS_OK) {
Serial.print(F("MIFARE_Write() failed: "));
Serial.println(mfrc522.GetStatusCodeName(status));
return;
}
else Serial.println(F("MIFARE_Write() success: "));
Serial.println(" ");
mfrc522.PICC_HaltA(); // Halt PICC
mfrc522.PCD_StopCrypto1();  // Stop encryption on PCD
}
void dump_byte_array(byte *buffer, byte bufferSize) {
for (byte i = 0; i < bufferSize; i++) {
Serial.print(buffer[i] < 0x10 ? " 0" : " ");
Serial.print(buffer[i], HEX);
}
}
void dump_byte_array1(byte *buffer, byte bufferSize) {
for (byte i = 0; i < bufferSize; i++) {
Serial.print(buffer[i] < 0x10 ? " 0" : " ");
Serial.write(buffer[i]);
}
}
void start(){
choice = Serial.read();
if(choice == '1')
{
Serial.println("Read dump the card");
DumpInfo();
}
else if(choice == '2')
{
Serial.println("Read personal data");
Read_card();
}
else if(choice == '3')
{
Serial.println("Write personal data");
Write_card();
}
}

Checking the results
To write the firmware, open the Arduino IDE, connect the Arduino development board. Let's select the appropriate board models and the port (one of the most common mistakes beginners make) is the wrong connection port. Press the arrow button. In case of successful registration to the board, we will see the corresponding message Done uploading at the bottom of the screen. In case of an error, the details will be displayed in red.
After successful compilation, you need to solder the MFRC module to the board, according to the scheme indicated earlier. You can also solder before recording, the main thing is to disconnect it from the computer during soldering. Then you can immediately connect the device to your smartphone using an adapter, or you can use the terminal in the Arduino IDE. In my examples below, I will give screenshots from the phone. The phone must have a Serial client installed, for example Serial USB Terminal. We open the client, specify the port and connect. If everything went well, we will see the following picture.

As a first example, consider a card for one metro ride (used).
For transport cards only sectors 0 and 15 of the card are used. Zero sector is special and in its zero block a unique identifier of the card is stored, which is used to distinguish it from others. At 15, metro-specific information is stored. Press 1 and bring the card to the reader. Sometimes the data is not read immediately, you will have to swipe the card several times. As a result, we get the following dump.

1581605992003-png.38259


Since the card has been used for a long time, it makes no sense to hide any data from it.
For comparison, the card for the passage of a child to school contains (or rather may contain) clearly more data.

1581605992040.png


Now let's try to read and write personal information on our card.
The reading process is quite simple - press 2 and present the card. In my example, both fields are codeby.net.

1581605992072-png.38256


The recording process is a little more complicated: we need to have time to enter the name within the specified time interval, otherwise we will receive an error.

1581605992104-png.38257


You can check the success of the recording by pressing 2 again and making sure that the previously recorded information is saved on the card.

What is the bottom line
As a result, we got a portable device that allows us to read and write data to a contactless card. For a number of reasons, I didn't touch on the topic of changing the UID. Those interested can independently study the examples supplied with the MFRC522 library. I will only note that the proximity card that comes with the reader does not allow you to overwrite UIDs. In order to rewrite them, you need to purchase special rewritable cards from a well-known Chinese site. In order to make sure that the card being sold is really rewritable, you need to look at the reviews. If the seller is cheating, buyers will definitely write, the opposite is also true.
Otherwise, the device can be used, for example, to back up the contents of contactless cards.

codeby.net
 
Top