Статья Собираем донгл для работы с бесконтактными картами

Собираем донгл для работы с бесконтактными картами

Вступление
Бесконтактные карты давно уже стали неотъемлемой частью нашей жизни. С их помощью мы проходим на работу, пользуемся общественным транспортом, используем в качестве ключей. В этой статье я предлагаю подробно рассмотреть как теоретические основы работы бесконтактных карт, так и на практике собрать устройство для чтения и записи данных на карты. В качестве аппаратной платформы нам потребуется Arduino Nano и модуль для считывания прошивок, а также переходник miniUSB-microUSB/Type C для взаимодействия со смартфоном.
В статье речь пойдет о низкоуровневой работе с картами диапазона 13,56 МГц на уровне байт. Поэтому я не буду касаться технологии NFC и функционала смартфонов по работе с картами через NFC с помощью приложений типа NFC ReTag.
Описанные в статье решения и технологии могут быть полезны как системным администраторам и сотрудникам служб безопасности, отвечающим за пропускной режим на объекте, так и специалистам по ИБ, осуществляющим тестирования на проникновение.
Однако, как обычно напоминаю, что вся информация, представленная в статье приводится исключительно с ознакомительной целью. По этой же причине я сознательно опускаю реализацию части функций в описываемом устройстве.
Теория бесконтактных карт
В этом разделе мы поговорим о том, какие бывают карты. Бесконтактные карты бывают двух диапазонов 125 кГц, и 13,56 МГц (более экзотические диапазоны рассматривать не будем) и действуют на расстоянии от 0 до 15 см. Принцип действия построен на использовании колебательного контура, базовыми компонентами которого являются микросхема, конденсатор и катушка индуктивности.
Считыватель излучает магнитное поле с помощью катушки индуктивности, тем самым передавая ток и заряжая конденсатор, который питает электроэнергией микросхему. Обмен информацией между картой и считывающим устройством осуществляется через эту же катушку посредством модуляции колебаний электромагнитного поля устройства. В простейшем случае карта циклически непрерывно передаёт только свой уникальный номер. Однако, как мы увидим далее, возможен и более сложный обмен информацией по принципу запрос-ответ. Многие карты обладают небольшой флеш-памятью порядка 1 Кб, в которую можно записывать данные.
Далее речь пойдет только о 13,56 МГц, так как карты диапазона 125 КГц считаются устаревшими (хотя в России активно используются) и небезопасными. Однако, если в процессе пентеста вам потребуется проверить защищенность СКУД на основе карт этого диапазона, то можно воспользоваться вот этим готовым копировальщиком:

Бесконтактная карта с рабочим диапазоном 13,56 МГц представляет собой, по сути один килобайт энергонезависимой памяти. Он делится на 16 секторов по 4 16-байтных блока в каждом. Блок - наименьшая адресуемая единица при работе с картой. Сектор - единица, с которой сопоставляются отдельные права доступа и ключи для проведения операций. Каждый сектор хранит собственную пару ключей, а права доступа указывают, какой доступ при указании какого ключа возможен. Для взаимодействия с картой используется протокол PICC, позволяющий получить данные с карты.
Алгоритм активации PICC в соответствии со стандартом ISO 14443 представлен на следующем рисунке:

1581605991878.png


В результате активации PICC мы получим примерно следующие данные:
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


Так как, при написании кода мы будем использовать библиотеку, уже содержащую реализацию всех необходимых для работы PICC операций, а не вижу смысла более подробно рассматривать процесс активации и взаимодействия по протоколу PICC. Вместо этого я предлагаю перейти к практической части. Но желающим ознакомиться с занудной теорией предлагается почитать описание протокола 14443A ( ГОСТ Р ИСО/МЭК 14443-3-2014 Карты идентификационные. Карты на интегральных схемах бесконтактные. Карты близкого действия.)


Концепция донглов
Мы привыкли к тому, что любое устройство должно быть самодостаточным. Заряженный телефон, планшет, ноутбук все эти устройства являются самодостаточными и будучи заряженными не требуют каких-либо дополнительных аппаратных компонентов для работы. Другое дело, это самодельные устройства, заточенные под выполнение конкретных задач. Для взаимодействия с пользователем им нужны кнопки, выключатели, дисплей (хотя бы ЖК), источник питания. Все это усложняет конструкцию, (необходимо использовать большее число деталей, паять больше контактов) создает больше точек отказа (может отказать больше деталей и контактов), увеличивает габариты и массу устройства (для портативного устройства размеры имеют значение), наконец увеличивают стоимость реализации (стоимость ЖК дисплея вместе с аккумулятором и модулем управления питанием и корпусом большего размера будет больше стоимости Ардуины).
Теперь вспомним что у любого из нас всегда есть с собой смартфон, который может предоставить все необходимые ресурсы (интерфейс взаимодействия, питание) для нашего устройства.
Слово dongle англоязычная википедия трактует как устройство, которое будучи подключенным к порту другого устройства предоставляет ему дополнительную функциональность. Предлагаю более пристально посмотреть на концепцию устройств, подключаемых к смартфонам. Такая концепция существенно упрощает сборку и повышает надежность, не требуя взамен серьезных ресурсов от смартфона. Достаточно лишь через переходник USB-microUSB или type-C (зависит от смартфона) подключить донгл и с помощью USB-Serial клиента управлять им. В своих последующих статьях я планирую еще не раз использовать донглы для различных задач.
Приступаем к сборке
Четко сформулируем требования и приступим к сборке. Итак, наше устройство должно подключаться через microUSB/type-C к телефону. В приложении USB-Serial мы подключаемся к нужному порту. Далее должно открыться меню, в котором мы можем выбрать нужный режим.
Предлагается реализовать следующие режимы:
  1. Чтение содержимого карты
  2. Чтение персональных данных с карты
  3. Запись персональных данных на карту
Отличие первого от второго пунктов заключается в том, что в первом случае мы читаем все данные с карты (делаем дамп), тогда, как во втором мы считываем только значения полей, в которые записываются данные владельца карты, как правило имя и фамилия.
Вот что выдадут для одной и той же карты выбор первого и второго пунктов:

Для сборки демонстрационного устройства данного функционала вполне хватит. Теперь, поговорим о том, какие компоненты нам потребуются. Вот все что нужно:
  1. Arduino Nano (с не припаянными разъемами) –
  2. Модуль MFRC-522 -
  3. Переходник miniUSB->microUSB/Typec-C
В качестве ядра устройства будет выступать Arduino Nano. Я крайне не рекомендую использовать более мощные Ардуины по нескольким причинам. Прежде всего, для наших задач Nano вполне достаточно, так как мы делаем портативное устройство и размер имеет большое значение, а Nano достаточно маленькая. Также, Arduino Nano можно приобрести с не припаянными штекерами. В готовых к использованию устройствах я никогда не использую беспаечные соединения, так как их надежность в боевых условиях стремится к нулю. Беспаечные платы хороши только для быстрого прототипирования. Для реальных устройств только паяльник. Не поленитесь научиться нормально паять – навык в хозяйстве пригодится.
К Arduino необходимо припаять модуль для работы с бесконтактными картами в соответствии со следующей схемой.
1581605991935.png

Далее нам необходимо написать прошивку для нашего донгла. В качестве средства разработки мы будем использовать среду Arduino IDE. Тем, кто вообще не знаком с Arduino, я рекомендую поискать в интернете статьи по работе с данной макетной платой, так как обучение основам не входит в рамки данной статьи.
Если вы до этого не работали с RFID, необходимые библиотеки у вас скорее всего не установлены. Поэтому перед написания кода установим необходимые библиотеки. Для этого необходимо скачать файл https://github.com/miguelbalboa/rfid/archive/master.zip и распаковать содержимое архива в папку %Arduino_IDE%/libraries. Теперь, после запуска среды Arduino IDE, у нас в разделе Files -> Examples -> MFRC522 должны появиться несколько скетчей с примерами работы с картой.
Теперь перейдем непосредственно к кодингу. Прежде всего реализуем меню для взаимодействия по Serial порту. Для этого в стандартной процедуре loop выведем на экран опции меню и будем ожидать пользовательского ввода.
C:
    Serial.println("1. Read dump the card");
    Serial.println("2. Read personal data");
    Serial.println("3. Write personal data");
В зависимости от выбранных пользователем пунктов запускаем соответствующие процедуры. Алгоритмы работы с картами построены на основе примеров, входящих в состав библиотеки MFRC522, поэтому, в случае возникновения проблем, вы можете проверить работу отдельных процедур непосредственно в примерах.
Здесь же я приведу исходный код процедур из своего кода.
Чтение содержимого карты
Первым делом нам необходимо убедиться в наличии карты и возможности считать данные с нее. Для этого воспользуемся значениями PICC_IsNewCardPresent() и PICC_ReadCardSerial(), если хотя бы одно из них неопределено – выходим. Если все хорошо выводим на серийный порт содержимое карты по ее UID.
Хочу отметить, что считывание данных с карты и вывод их на экран займет несколько секунд, поэтому при тестировании не торопитесь убирать карту от считывателя.
Процедура чтения дампа с карты будет иметь следующий вид.
C:
void DumpInfo() {
  delay(pause);
  if ( ! mfrc522.PICC_IsNewCardPresent()) {
    return;
  }
  if ( ! mfrc522.PICC_ReadCardSerial()) {
    return;
  }
  mfrc522.PICC_DumpToSerial(&(mfrc522.uid));  
  mfrc522.PICC_HaltA();  
}

Чтение персональной информации
В отличии от чтения дампа карты, при считывании персональной информации нам придется разбирать те данные, которые мы считываем. Под персональной информацией в данном случае мы будем подразумевать имя (First Name) и фамилию (Last Name) владельца карты. Проверив доступность карты по аналогии с предыдущим примером, мы считываем дамп и начинаем его разбирать. Для считывания дампа нам необходимо сначала подобрать ключ для расшифровки данных. В большинстве случаев никто не меняет заводские ключи, поэтому мы используем восемь ключей, указанных в массиве key. Извлеченное First Name и Last Name владельца карты мы поочередно поместим в массивы buffer1 и buffer2, длиной 18 байт.
Кстати, по этим ссылкам можно найти еще много заводских ключей. ikarus23/MifareClassicTool Обратите внимание на последние разделы списка. Интересно, о каких карточках для публичных туалетов идет речь?
.
Вот исходный код процедуры чтения персональной информации владельца карты.
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();  
}
Запись на карту
Теперь рассмотрим запись персональной информации на карту. Здесь мы будем получать строку, вводимую пользователем в массив buf. На ввод этой информации мы будем давать 20 секунд. Замечу, что на практике этого времени может оказаться недостаточно для набора текста на смартфоне, поэтому, возможно, значение стоит увеличить. После ввода First или Last Name обязательно надо указывать символ #.
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
}
Обработчик выбранных пользователем опций меню мы поместим в отдельную процедуру Start(). Здесь ничего сложного нет, просто ожидаем пользовательский ввод и в случае соответствия вызываем нужную процедуру.

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();
    }
}
В итоге, исходный код всей прошивки имеет следующий вид:
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();
    }
  }

Проверка результатов
Для записи прошивки откроем среду разработки Arduino IDE, подключим макетную плату Arduino. Выберем соответствующие модели платы и порт (одна из самых частых ошибок новичков) это неправильный порт подключения. Нажмем кнопку со стрелкой. В случае успешной записи на плату мы увидим в нижней части экрана соответствующее сообщение Done uploading. В случае ошибки подробности будут выведены красным цветом.
После успешной компиляции необходимо припаять модуль MFRC к плате, по схеме указанной ранее. Можно припаять и до записи, главное отключить от компьютера на время пайки. Далее можно сразу подключить устройство к смартфону с помощью переходника, а можно воспользоваться терминалом в Arduino IDE. В своих примерах далее я приведу скриншоты с телефона. На телефон должен быть установлен Serial клиент, например Serial USB Terminal. Открываем клиента, указываем порт и подключаемся. Если все прошло успешно, мы увидим следующую картинку.

В качестве первого примера рассмотрим карту на один проезд на метро (использованную).
Для транспортных карт используются только секторы 0 и 15 карты. Нулевой сектор - специальный и в его нулевом блоке хранится уникальный идентификатор карты, который используется для того, чтобы отличать ее от других. В 15, сохраняется специфичная для метрополитена информация. Нажимаем 1 и подносим карту к считывателю. Иногда данные считываются не сразу, придется поводить карточкой несколько раз. В итоге получаем следующий дамп.
1581605992003.png

Так как карта уже давно использовалась скрывать какие-либо данные с нее нет смысла.
Для сравнения, карта для прохода ребенка в школу содержит (точнее может содержать) явно больше данных.

1581605992040.png

Теперь попробуем прочитать и записать персональную информацию на нашу карту.
Процесс считывания довольно прост – нажимаем 2 и подносим карту. В моем примере в обоих полях указано codeby.net.

1581605992072.png


Процесс записи немного сложнее: нам надо успеть ввести имя за указанный интервал времени, иначе мы получим ошибку.

1581605992104.png


Проверить успешность записи можно снова нажав 2 и убедившись в том, что записанная ранее информация сохранилась на карте.

Что в итоге
В итоге мы получили портативное устройство, позволяющее считывать и записывать данные на бесконтактную карту. В силу ряда соображений я не стал касаться темы смены UID. Желающие могут самостоятельно изучить примеры, поставляемые вместе с библиотекой MFRC522. Замечу лишь, что бесконтактная карта, идущая вместе с считывателем, не позволяет перезаписывать идентификаторы UID. Для того, чтобы их перезаписывать, необходимо приобрести специальные, перезаписываемые карты, на известной китайской площадке. Для того, чтобы убедиться, что продаваемая карта действительно является перезаписываемой, нужно посмотреть на отзывы. Если продавец обманывает, покупатели обязательно напишут, обратное тоже верно.
В остальном, устройство может быть использовано например для бэкапа содержимого бесконтактных карт.
 

Евгений Л

New member
21.04.2019
4
0
BIT
6
По идее если еще на Soft serial повесить блю туз модуль,то можно сделать девайс автономным.Или как вариант использовать esp32 ,тогда можно и по зубу и по вафле конектить.
 
Мы в соцсетях:

Обучение наступательной кибербезопасности в игровой форме. Начать игру!