Собираем донгл для работы с бесконтактными картами
Вступление
Бесконтактные карты давно уже стали неотъемлемой частью нашей жизни. С их помощью мы проходим на работу, пользуемся общественным транспортом, используем в качестве ключей. В этой статье я предлагаю подробно рассмотреть как теоретические основы работы бесконтактных карт, так и на практике собрать устройство для чтения и записи данных на карты. В качестве аппаратной платформы нам потребуется 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 представлен на следующем рисунке:
В результате активации 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 мы подключаемся к нужному порту. Далее должно открыться меню, в котором мы можем выбрать нужный режим.
Предлагается реализовать следующие режимы:
Вот что выдадут для одной и той же карты выбор первого и второго пунктов:
Для сборки демонстрационного устройства данного функционала вполне хватит. Теперь, поговорим о том, какие компоненты нам потребуются. Вот все что нужно:
К Arduino необходимо припаять модуль для работы с бесконтактными картами в соответствии со следующей схемой.
Далее нам необходимо написать прошивку для нашего донгла. В качестве средства разработки мы будем использовать среду Arduino IDE. Тем, кто вообще не знаком с Arduino, я рекомендую поискать в интернете статьи по работе с данной макетной платой, так как обучение основам не входит в рамки данной статьи.
Если вы до этого не работали с RFID, необходимые библиотеки у вас скорее всего не установлены. Поэтому перед написания кода установим необходимые библиотеки. Для этого необходимо скачать файл https://github.com/miguelbalboa/rfid/archive/master.zip и распаковать содержимое архива в папку %Arduino_IDE%/libraries. Теперь, после запуска среды Arduino IDE, у нас в разделе Files -> Examples -> MFRC522 должны появиться несколько скетчей с примерами работы с картой.
Теперь перейдем непосредственно к кодингу. Прежде всего реализуем меню для взаимодействия по Serial порту. Для этого в стандартной процедуре loop выведем на экран опции меню и будем ожидать пользовательского ввода.
В зависимости от выбранных пользователем пунктов запускаем соответствующие процедуры. Алгоритмы работы с картами построены на основе примеров, входящих в состав библиотеки MFRC522, поэтому, в случае возникновения проблем, вы можете проверить работу отдельных процедур непосредственно в примерах.
Здесь же я приведу исходный код процедур из своего кода.
Чтение содержимого карты
Первым делом нам необходимо убедиться в наличии карты и возможности считать данные с нее. Для этого воспользуемся значениями PICC_IsNewCardPresent() и PICC_ReadCardSerial(), если хотя бы одно из них неопределено – выходим. Если все хорошо выводим на серийный порт содержимое карты по ее UID.
Хочу отметить, что считывание данных с карты и вывод их на экран займет несколько секунд, поэтому при тестировании не торопитесь убирать карту от считывателя.
Процедура чтения дампа с карты будет иметь следующий вид.
Чтение персональной информации
В отличии от чтения дампа карты, при считывании персональной информации нам придется разбирать те данные, которые мы считываем. Под персональной информацией в данном случае мы будем подразумевать имя (First Name) и фамилию (Last Name) владельца карты. Проверив доступность карты по аналогии с предыдущим примером, мы считываем дамп и начинаем его разбирать. Для считывания дампа нам необходимо сначала подобрать ключ для расшифровки данных. В большинстве случаев никто не меняет заводские ключи, поэтому мы используем восемь ключей, указанных в массиве key. Извлеченное First Name и Last Name владельца карты мы поочередно поместим в массивы buffer1 и buffer2, длиной 18 байт.
Кстати, по этим ссылкам можно найти еще много заводских ключей. ikarus23/MifareClassicTool Обратите внимание на последние разделы списка. Интересно, о каких карточках для публичных туалетов идет речь?
Вот исходный код процедуры чтения персональной информации владельца карты.
Запись на карту
Теперь рассмотрим запись персональной информации на карту. Здесь мы будем получать строку, вводимую пользователем в массив buf. На ввод этой информации мы будем давать 20 секунд. Замечу, что на практике этого времени может оказаться недостаточно для набора текста на смартфоне, поэтому, возможно, значение стоит увеличить. После ввода First или Last Name обязательно надо указывать символ #.
Обработчик выбранных пользователем опций меню мы поместим в отдельную процедуру Start(). Здесь ничего сложного нет, просто ожидаем пользовательский ввод и в случае соответствия вызываем нужную процедуру.
В итоге, исходный код всей прошивки имеет следующий вид:
Проверка результатов
Для записи прошивки откроем среду разработки Arduino IDE, подключим макетную плату Arduino. Выберем соответствующие модели платы и порт (одна из самых частых ошибок новичков) это неправильный порт подключения. Нажмем кнопку со стрелкой. В случае успешной записи на плату мы увидим в нижней части экрана соответствующее сообщение Done uploading. В случае ошибки подробности будут выведены красным цветом.
После успешной компиляции необходимо припаять модуль MFRC к плате, по схеме указанной ранее. Можно припаять и до записи, главное отключить от компьютера на время пайки. Далее можно сразу подключить устройство к смартфону с помощью переходника, а можно воспользоваться терминалом в Arduino IDE. В своих примерах далее я приведу скриншоты с телефона. На телефон должен быть установлен Serial клиент, например Serial USB Terminal. Открываем клиента, указываем порт и подключаемся. Если все прошло успешно, мы увидим следующую картинку.
В качестве первого примера рассмотрим карту на один проезд на метро (использованную).
Для транспортных карт используются только секторы 0 и 15 карты. Нулевой сектор - специальный и в его нулевом блоке хранится уникальный идентификатор карты, который используется для того, чтобы отличать ее от других. В 15, сохраняется специфичная для метрополитена информация. Нажимаем 1 и подносим карту к считывателю. Иногда данные считываются не сразу, придется поводить карточкой несколько раз. В итоге получаем следующий дамп.
Так как карта уже давно использовалась скрывать какие-либо данные с нее нет смысла.
Для сравнения, карта для прохода ребенка в школу содержит (точнее может содержать) явно больше данных.
Теперь попробуем прочитать и записать персональную информацию на нашу карту.
Процесс считывания довольно прост – нажимаем 2 и подносим карту. В моем примере в обоих полях указано codeby.net.
Процесс записи немного сложнее: нам надо успеть ввести имя за указанный интервал времени, иначе мы получим ошибку.
Проверить успешность записи можно снова нажав 2 и убедившись в том, что записанная ранее информация сохранилась на карте.
Что в итоге
В итоге мы получили портативное устройство, позволяющее считывать и записывать данные на бесконтактную карту. В силу ряда соображений я не стал касаться темы смены UID. Желающие могут самостоятельно изучить примеры, поставляемые вместе с библиотекой MFRC522. Замечу лишь, что бесконтактная карта, идущая вместе с считывателем, не позволяет перезаписывать идентификаторы UID. Для того, чтобы их перезаписывать, необходимо приобрести специальные, перезаписываемые карты, на известной китайской площадке. Для того, чтобы убедиться, что продаваемая карта действительно является перезаписываемой, нужно посмотреть на отзывы. Если продавец обманывает, покупатели обязательно напишут, обратное тоже верно.
В остальном, устройство может быть использовано например для бэкапа содержимого бесконтактных карт.
Вступление
Бесконтактные карты давно уже стали неотъемлемой частью нашей жизни. С их помощью мы проходим на работу, пользуемся общественным транспортом, используем в качестве ключей. В этой статье я предлагаю подробно рассмотреть как теоретические основы работы бесконтактных карт, так и на практике собрать устройство для чтения и записи данных на карты. В качестве аппаратной платформы нам потребуется 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 представлен на следующем рисунке:
В результате активации 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 мы подключаемся к нужному порту. Далее должно открыться меню, в котором мы можем выбрать нужный режим.
Предлагается реализовать следующие режимы:
- Чтение содержимого карты
- Чтение персональных данных с карты
- Запись персональных данных на карту
Вот что выдадут для одной и той же карты выбор первого и второго пунктов:
Для сборки демонстрационного устройства данного функционала вполне хватит. Теперь, поговорим о том, какие компоненты нам потребуются. Вот все что нужно:
- Arduino Nano (с не припаянными разъемами) –
Ссылка скрыта от гостей
- Модуль MFRC-522 -
Ссылка скрыта от гостей
- Переходник miniUSB->microUSB/Typec-C
К Arduino необходимо припаять модуль для работы с бесконтактными картами в соответствии со следующей схемой.
Далее нам необходимо написать прошивку для нашего донгла. В качестве средства разработки мы будем использовать среду 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");
Здесь же я приведу исходный код процедур из своего кода.
Чтение содержимого карты
Первым делом нам необходимо убедиться в наличии карты и возможности считать данные с нее. Для этого воспользуемся значениями 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
}
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 и подносим карту к считывателю. Иногда данные считываются не сразу, придется поводить карточкой несколько раз. В итоге получаем следующий дамп.
Так как карта уже давно использовалась скрывать какие-либо данные с нее нет смысла.
Для сравнения, карта для прохода ребенка в школу содержит (точнее может содержать) явно больше данных.
Теперь попробуем прочитать и записать персональную информацию на нашу карту.
Процесс считывания довольно прост – нажимаем 2 и подносим карту. В моем примере в обоих полях указано codeby.net.
Процесс записи немного сложнее: нам надо успеть ввести имя за указанный интервал времени, иначе мы получим ошибку.
Проверить успешность записи можно снова нажав 2 и убедившись в том, что записанная ранее информация сохранилась на карте.
Что в итоге
В итоге мы получили портативное устройство, позволяющее считывать и записывать данные на бесконтактную карту. В силу ряда соображений я не стал касаться темы смены UID. Желающие могут самостоятельно изучить примеры, поставляемые вместе с библиотекой MFRC522. Замечу лишь, что бесконтактная карта, идущая вместе с считывателем, не позволяет перезаписывать идентификаторы UID. Для того, чтобы их перезаписывать, необходимо приобрести специальные, перезаписываемые карты, на известной китайской площадке. Для того, чтобы убедиться, что продаваемая карта действительно является перезаписываемой, нужно посмотреть на отзывы. Если продавец обманывает, покупатели обязательно напишут, обратное тоже верно.
В остальном, устройство может быть использовано например для бэкапа содержимого бесконтактных карт.