You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
204 lines
6.4 KiB
204 lines
6.4 KiB
3 years ago
|
#include "MFRC522Hack.h"
|
||
|
|
||
|
/**
|
||
|
* Performs the "magic sequence" needed to get Chinese UID changeable
|
||
|
* Mifare cards to allow writing to sector 0, where the card UID is stored.
|
||
|
*
|
||
|
* Note that you do not need to have selected the card through REQA or WUPA,
|
||
|
* this sequence works immediately when the card is in the reader vicinity.
|
||
|
* This means you can use this method even on "bricked" cards that your reader does
|
||
|
* not recognise anymore (see MFRC522Hack::MIFARE_UnbrickUidSector).
|
||
|
*
|
||
|
* Of course with non-bricked devices, you're free to select them before calling this function.
|
||
|
*/
|
||
|
bool MFRC522Hack::MIFARE_OpenUidBackdoor(const bool logErrors) const {
|
||
|
// Magic sequence:
|
||
|
// > 50 00 57 CD (HALT + CRC)
|
||
|
// > 40 (7 bits only)
|
||
|
// < A (4 bits only)
|
||
|
// > 43
|
||
|
// < A (4 bits only)
|
||
|
// Then you can write to sector 0 without authenticating
|
||
|
|
||
|
_device->PICC_HaltA(); // 50 00 57 CD
|
||
|
|
||
|
byte cmd = 0x40;
|
||
|
byte validBits = 7; /* Our command is only 7 bits. After receiving card response,
|
||
|
this will contain amount of valid response bits. */
|
||
|
byte response[32]; // Card's response is written here
|
||
|
byte received;
|
||
|
MFRC522::StatusCode status = _device->PCD_TransceiveData(&cmd, (byte) 1, response, &received, &validBits, (byte) 0,
|
||
|
false); // 40
|
||
|
if (status != MFRC522::STATUS_OK) {
|
||
|
if (logErrors) {
|
||
|
Serial.println(
|
||
|
F("Card did not respond to 0x40 after HALT command. Are you sure it is a UID changeable one?"));
|
||
|
Serial.print(F("Error name: "));
|
||
|
Serial.println(MFRC522Debug::GetStatusCodeName(status));
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
if (received != 1 || response[0] != 0x0A) {
|
||
|
if (logErrors) {
|
||
|
Serial.print(F("Got bad response on backdoor 0x40 command: "));
|
||
|
Serial.print(response[0], HEX);
|
||
|
Serial.print(F(" ("));
|
||
|
Serial.print(validBits);
|
||
|
Serial.print(F(" valid bits)\r\n"));
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
cmd = 0x43;
|
||
|
validBits = 8;
|
||
|
status = _device->PCD_TransceiveData(&cmd, (byte) 1, response, &received, &validBits, (byte) 0, false); // 43
|
||
|
if (status != MFRC522::STATUS_OK) {
|
||
|
if (logErrors) {
|
||
|
Serial.println(F("Error in communication at command 0x43, after successfully executing 0x40"));
|
||
|
Serial.print(F("Error name: "));
|
||
|
Serial.println(MFRC522Debug::GetStatusCodeName(status));
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
if (received != 1 || response[0] != 0x0A) {
|
||
|
if (logErrors) {
|
||
|
Serial.print(F("Got bad response on backdoor 0x43 command: "));
|
||
|
Serial.print(response[0], HEX);
|
||
|
Serial.print(F(" ("));
|
||
|
Serial.print(validBits);
|
||
|
Serial.print(F(" valid bits)\r\n"));
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// You can now write to sector 0 without authenticating!
|
||
|
return true;
|
||
|
} // End MIFARE_OpenUidBackdoor()
|
||
|
|
||
|
/**
|
||
|
* Reads entire block 0, including all manufacturer data, and overwrites
|
||
|
* that block with the new UID, a freshly calculated BCC, and the original
|
||
|
* manufacturer data.
|
||
|
*
|
||
|
* It assumes a default KEY A of 0xFFFFFFFFFFFF.
|
||
|
* Make sure to have selected the card before this function is called.
|
||
|
*/
|
||
|
bool MFRC522Hack::MIFARE_SetUid(const byte *newUid, const byte uidSize, const bool logErrors) const {
|
||
|
|
||
|
// UID + BCC byte can not be larger than 16 together
|
||
|
if (!newUid || !uidSize || uidSize > 15) {
|
||
|
if (logErrors) {
|
||
|
Serial.println(F("New UID buffer empty, size 0, or size > 15 given"));
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// Authenticate for reading
|
||
|
MFRC522::MIFARE_Key key = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
|
||
|
MFRC522::StatusCode status = _device->PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, (byte) 1, &key, &(_device->uid));
|
||
|
if (status != MFRC522::STATUS_OK) {
|
||
|
|
||
|
if (status == MFRC522::STATUS_TIMEOUT) {
|
||
|
// We get a read timeout if no card is selected yet, so let's select one
|
||
|
|
||
|
// Wake the card up again if sleeping
|
||
|
// byte atqa_answer[2];
|
||
|
// byte atqa_size = 2;
|
||
|
// PICC_WakeupA(atqa_answer, &atqa_size);
|
||
|
|
||
|
if (!_device->PICC_IsNewCardPresent() || !_device->PICC_ReadCardSerial()) {
|
||
|
Serial.println(F("No card was previously selected, and none are available. Failed to set UID."));
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
status = _device->PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, (byte) 1, &key, &(_device->uid));
|
||
|
if (status != MFRC522::STATUS_OK) {
|
||
|
// We tried, time to give up
|
||
|
if (logErrors) {
|
||
|
Serial.println(F("Failed to authenticate to card for reading, could not set UID: "));
|
||
|
Serial.println(MFRC522Debug::GetStatusCodeName(status));
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
} else {
|
||
|
if (logErrors) {
|
||
|
Serial.print(F("PCD_Authenticate() failed: "));
|
||
|
Serial.println(MFRC522Debug::GetStatusCodeName(status));
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Read block 0
|
||
|
byte block0_buffer[18];
|
||
|
byte byteCount = sizeof(block0_buffer);
|
||
|
status = _device->MIFARE_Read((byte) 0, block0_buffer, &byteCount);
|
||
|
if (status != MFRC522::STATUS_OK) {
|
||
|
if (logErrors) {
|
||
|
Serial.print(F("MIFARE_Read() failed: "));
|
||
|
Serial.println(MFRC522Debug::GetStatusCodeName(status));
|
||
|
Serial.println(F("Are you sure your KEY A for sector 0 is 0xFFFFFFFFFFFF?"));
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// Write new UID to the data we just read, and calculate BCC byte
|
||
|
byte bcc = 0;
|
||
|
for (uint8_t i = 0; i < uidSize; i++) {
|
||
|
block0_buffer[i] = newUid[i];
|
||
|
bcc ^= newUid[i];
|
||
|
}
|
||
|
|
||
|
// Write BCC byte to buffer
|
||
|
block0_buffer[uidSize] = bcc;
|
||
|
|
||
|
// Stop encrypted traffic so we can send raw bytes
|
||
|
_device->PCD_StopCrypto1();
|
||
|
|
||
|
// Activate UID backdoor
|
||
|
if (!MIFARE_OpenUidBackdoor(logErrors)) {
|
||
|
if (logErrors) {
|
||
|
Serial.println(F("Activating the UID backdoor failed."));
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// Write modified block 0 back to card
|
||
|
status = _device->MIFARE_Write((byte) 0, block0_buffer, (byte) 16);
|
||
|
if (status != MFRC522::STATUS_OK) {
|
||
|
if (logErrors) {
|
||
|
Serial.print(F("MIFARE_Write() failed: "));
|
||
|
Serial.println(MFRC522Debug::GetStatusCodeName(status));
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// Wake the card up again
|
||
|
byte atqa_answer[2];
|
||
|
byte atqa_size = 2;
|
||
|
_device->PICC_WakeupA(atqa_answer, &atqa_size);
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Resets entire sector 0 to zeroes, so the card can be read again by readers.
|
||
|
*/
|
||
|
bool MFRC522Hack::MIFARE_UnbrickUidSector(const bool logErrors) const {
|
||
|
MIFARE_OpenUidBackdoor(logErrors);
|
||
|
|
||
|
byte block0_buffer[] = {0x01, 0x02, 0x03, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
|
0x00};
|
||
|
|
||
|
// Write modified block 0 back to card
|
||
|
MFRC522::StatusCode status = _device->MIFARE_Write((byte) 0, block0_buffer, (byte) 16);
|
||
|
if (status != MFRC522::STATUS_OK) {
|
||
|
if (logErrors) {
|
||
|
Serial.print(F("MIFARE_Write() failed: "));
|
||
|
Serial.println(MFRC522Debug::GetStatusCodeName(status));
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
return true;
|
||
|
}
|