Collecting a dongle for working with contactless cards

Lord777

Professional
Messages
2,583
Reputation
15
Reaction score
1,302
Points
113
Introduction
Contactless cards have long been an integral part of our lives. We use them to get to work, use public transport, and use them as keys. In this article, I propose to consider in detail both the theoretical foundations of contactless cards, and in practice to build a device for reading and writing data to cards. As a hardware platform, we will 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 operation with 13.56 MHz band cards 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 this article can be useful for both system administrators and security personnel responsible for access control at the facility, as well as for information security specialists performing penetration testing.
However, as usual, I would like to remind you that all the information provided in this article is provided for informational purposes only. For the same reason, I deliberately omit the implementation of some functions in the described device.

Contactless card theory
In this section, we'll talk about what types of cards there are. Contactless cards come in two bands: 125 kHz and 13.56 MHz (we will not consider more exotic bands) and operate at a distance of 0 to 15 cm. The principle of operation is based on the use of an oscillatory 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 supplies electricity to the chip. Information exchange between the card and the reader is carried out through the same coil by modulating the fluctuations of the electromagnetic field of the device. In the simplest case, the card cyclically continuously transmits only its unique number. However, as we will see later, a more complex request-response exchange of information is also possible. Many cards have a small flash memory of the order of 1 KB, in which you can write data.
Further, we will only talk about 13.56 MHz, since 125 kHz band cards are considered outdated (although they are actively used in Russia) and unsafe. However, if during the pentest process you need to check the security of the ACS based on cards 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. Block - the smallest addressable unit when working with the card. A sector is a unit to which individual access rights and keys for performing operations are mapped. Each sector stores its own key pair, and access rights indicate which access is possible when specifying which key. To interact with the card, the PICC protocol is used, which allows you to get data from the card.
The PICC activation algorithm in accordance with the ISO 14443 standard is shown in the following figure:

As a result of PICC activation, we will get approximately the following data::
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
0000000000000000000000000000000000000000000000000000000000000000


Since, when writing code, we will use a library that already contains the implementation of all the operations necessary for PICC to work, and I don't see any point in considering the activation process and interaction over the PICC protocol in more detail. Instead, I suggest moving on to the practical part. But those who want 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. Integrated circuit cards are contactless. Close-range cards.)
GOST R ISO / IEC 14443-4-2014 Identification cards. Integrated circuit cards are contactless. Close-range cards. Part 4. Transmission Protocol, GOST R No. ISO / IEC 14443-4-2014 dated November 11, 2014

The concept of dongles
We are used to the fact that any device should be self-sufficient. Charged phone, tablet, laptop all these devices are self-sufficient and when charged do not require any additional hardware components to work. Another thing is that these are self-made devices that are sharpened to perform specific tasks. To interact with the user, they need buttons, switches, a display (at least an LCD), and a power source. All this complicates the design (you need to use more parts, solder more contacts) creates more points of failure (more parts and contacts can fail), increases the size and weight of the device (for a portable device, dimensions matter), and finally increases the cost of implementation (the cost of the LCD display together with the battery and power management module and the case a larger size will cost more than an Arduino).
Now let's remember that each of us always has a smartphone with us, which can provide all the necessary resources (interaction interface, power) for our device.
The word dongle is interpreted by Wikipedia in English as a device that, when connected to the port of another device, provides it with additional functionality. I suggest you take a closer look at the concept of devices connected to smartphones. This concept significantly simplifies assembly and increases reliability, without requiring serious resources from the smartphone in return. Just connect the dongle via a USB-microUSB or type-C adapter (depending on the smartphone) and use the USB-Serial client to manage it. In my future articles, I plan to use dongles for various tasks more than once.

Let's clearly formulate the requirements and start building. So, our device needs to connect via microUSB/type-C to the phone. In the USB-Serial app, we connect to the appropriate port. Next, a menu should open in which we can select the desired mode.

It is proposed to implement the following modes:
  1. Reading card content
  2. Reading personal data from a bank card
  3. Recording of personal data on a bank card
The difference between the first and second points is that in the first case we read all the data from the card (we do a dump), while in the second case we read only the values of the fields in which the cardholder's data is written, usually first and last name.
Here's what they'll give you for the same card choosing the first and second items:

This functionality is enough to build a demo device. Now, let's talk about what components we need. That's all you need:
  1. Arduino Nano (with non-soldered connectors)
  2. Module MFRC-522 -
  3. miniUSB->microUSB/Typec-C Adapter
The core of the device will be the Arduino Nano. I highly recommend not using more powerful Arduins for several reasons. First of all, Nano is quite sufficient for our tasks, since we are making a portable device and size is of great importance, and Nano is quite small. Also, the Arduino Nano can be purchased with unsoldered connectors. In ready-to-use devices, I never use solderless connections, since 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 solder normally – the skill will come in handy on the farm.
The contactless card module must be soldered to the Arduino according to the following diagram.

1581605991935-png.38258


Next, we need to write the firmware for our dongle. As a development tool, we will use the Arduino IDE. 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 not part of the scope of this article.
If you haven't worked with RFID before, you probably don't have the necessary libraries installed. Therefore, before writing the code, we will install the necessary libraries. To do this, download the file https://github.com/miguelbalboa/rfid/archive/master.zip and unzip the archive contents to 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 implement a menu for interaction over the Serial port. To do this, use the standard loop procedure to 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 start the corresponding procedures. Algorithms for working with cards are built on the basis of examples included in the MFRC522 library. Therefore, in case of problems, you can check the operation of individual procedures directly in the examples.
Here I will also provide the source code of the procedures from my own code.

Reading card content
First of all, we need to make sure that the card is available and that we can read data from it. To do this, use the values PICC_IsNewCardPresent() and PICC_ReadCardSerial(). If at least one of them is undefined, we exit. If everything is fine, we output the contents of the card to the serial port by its UID.
I would like to note that reading data from the card and displaying it on the screen will take several seconds, so when 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 analyze the data that we read. In this case, we will refer to personal information as the First Name and Last Name of the cardholder. After checking the availability of the card by analogy with the previous example, we read the dump and start parsing it. To read the dump, we first need to find the key to decrypt the data. In most cases, no one changes the factory keys, so we use the eight keys specified in the key array. The extracted First Name and Last Name of the cardholder will be placed alternately in the buffer1 and buffer2 arrays, which are 18 bytes long.
By the way, you can find many more factory keys on 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 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();
}

Write to the card
Now let's look at recording personal information on the card. Here we will get the string that the user enters in the buf array. We will give you 20 seconds to enter this information. I 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, you must specify the #character.
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, just wait for user input and, if it matches, call the desired 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 is as follows:
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 record the firmware, open the Arduino IDE development environment, connect the Arduino breadboard. Choose the appropriate board model and port (one of the most common mistakes beginners make) this is the wrong connection port. Click the arrow button. If you successfully upload to the board, you will see the corresponding Done uploading message at the bottom of the screen. If an error occurs, the details will be displayed in red.
After successful compilation, it is necessary to solder the MFRC module to the board, according to the scheme indicated earlier. You can solder it before recording, the main thing is to disconnect it from the computer for the duration of 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, such as Serial USB Terminal. 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).
Only sectors 0 and 15 of the card are used for transport cards. The zero sector is special and its zero block stores a unique card identifier, which is used to distinguish it from others. At 15, metro-specific information is saved. Press 1 and bring the card to the reader. Sometimes the data is not read immediately, and you will have to move 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, there is no point in hiding any data from it.
For comparison, the card for a child's passage to school contains (or may contain) clearly more data.

1581605992040-png.38254


Now let's try to read and write personal information to our card.
The reading process is quite simple – press 2 and bring up the card. In my example, both fields specify codeby.net.

1581605992072-png.38256


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

1581605992104-png.38257


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

What is the result
As a result, we got a portable device that allows you to read and write data to a contactless card. For a number of reasons, I did not touch on the topic of changing the UID. Those interested can study the examples provided with the MFRC522 library themselves. I will only note that the contactless card that goes with the reader does not allow you to overwrite UIDS. In order to rewrite them, you need to purchase special, rewritable cards on 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, the buyers will definitely write, and the opposite is also true.
Otherwise, the device can be used, for example, to backup the contents of contactless cards.
 
Top