아두이노로 RFID카드 복제하기

힘센캥거루
2025년 11월 26일
24
arduino

오늘은 아두이노로 RFID카드를 복제하는 방법에 대해 써보려고 한다.

한번 글을 쓰면 잊어버리지 않기에, 복기하는 차원에서 남긴다.

아두이노로 RFID카드 복제하기-1

1.RFID 카드의 내부 데이터 구조

일반적인 RFID 카드는 MIFARE Classic 1K 카드이다.

이 카드의 메모리 구조는 아래와 같다.

- 총 1024 bytes (1KB)
- 16개의 Sector (0~15)
- 각 Sector는 4개의 Block (Block 0~3)
- 각 Block은 16 bytes

각각의 섹터는 아래와 같은 구조를 가지고 있다.

Sector n
 ├── Block 0 (Data or UID block)
 ├── Block 1 (Data)
 ├── Block 2 (Data)
 └── Block 3 (Sector Trailer: Key A, Access Bits, Key B)

이런 섹터들 중에서 가장 중요한 의미를 가지는 곳은 섹터 0의 첫번째 데이터이다.

여기에 UID가 들어있다.

아두이노로 RFID카드 복제하기-2

2.UID

Unique IDentifier의 약자로, RFID 카드(예: 교통카드, 출입카드, 학생증 등)에 각 카드마다 부여된 고유 식별 번호를 뜻한다.

사람으로 치면 주민등록번호와 비슷한 역할이다.

UID는 Sector 0의 Block 0 내부 앞 4바이트에 저장된다.

[ UID0 | UID1 | UID2 | UID3 | BCC | Manufacturer Data… ]

RFID 기계가 카드를 읽을 때 가장 먼저 읽는 곳이 이 부분이다.

이걸 바탕으로 카드의 식별이 이루어진다.

아두이노로 RFID카드 복제하기-3

3. CUID 카드

중요한 것은 MIFARE Classic 1K(정품)의 UID는 공장에서 고정된다는 것이다.

NXP 정품 칩은 UID가 ROM에 저장되어 어떤 방법으로도 수정할 수 없다.

따라서 일반 카드 리더기나 MFRC522로는 UID 변조가 불가능하다.

아두이노로 RFID카드 복제하기-4

하지만 시장에는 UID 변경이 가능한 카드가 있는데 이런 카드를 CUID(일명 매직카드)라고 한다.

앞쪽의 C는 Changeable을 뜻한다.

외형은 MIFARE Classic 1K와 동일하지만, 내부 칩이 다음과 같이 다르다.

① Gen1A (UID/Backdoor 지원형)

  • 0x40 / 0x43 백도어 명령을 지원

  • UID 변경 명령을 별도로 제공

  • MFRC522 + Arduino 라이브러리의 MIFARE_SetUid()로 변경 가능

② CUID / Gen2 (Block 0 Writable)

  • 백도어 명령 없음

  • 대신 Block 0(UID가 들어있는 섹터)을 일반 쓰기(WRITE) 로 덮어쓸 수 있게 설계

  • MFRC522가 MIFARE_Write(0…) 명령을 받아주면 UID 변경 가능

  • 단, 모든 CUID가 되는 건 아니다. MFRC522가 지원 안 하면 PN532/ACR122U 필요

4. 아두이노로 RFID카드 복제하기

이제 원리는 알았으니 아두이노로 RFID카드를 복제하는 방법은 간단하다.

UID를 복제해주면 되는 것.

먼저 아두이노를 아래와 같이 연결해준다.

아두이노로 RFID카드 복제하기-5

Signal

MFRC522 Pin

Arduino Uno / 101

Arduino Mega

Arduino Nano v3

Arduino Leonardo / Micro

Arduino Pro Micro

RST / Reset

RST

9

5

D9

RESET / ICSP-5

RST

SPI SS

SDA (SS)

10

53

D10

10

10

SPI MOSI

MOSI

11 / ICSP-4

51

D11

ICSP-4

16

SPI MISO

MISO

12 / ICSP-1

50

D12

ICSP-1

14

SPI SCK

SCK

13 / ICSP-3

52

D13

ICSP-3

15

그리고 arduino IDE에서 MFRC522 라이브러리를 찾아 설치해준다.

아두이노로 RFID카드 복제하기-6

이제 파일 -> 에제 -> MFRC522에서 ReadNUID를 선택해준다.

아두이노로 RFID카드 복제하기-7

해당 코드를 업로드 후 UID 값을 먼저 읽는다.

UID는 일반적으로 16진수 4개로 이루어저 있다.

F5 5F 36 80

 만일 위와 같은 UID를 얻었다면, 업로드 할 UID는 아래와 같다.

숫자 앞의 0x는 이 숫자가 16진수라는 뜻이다.

0xF5 0x5F 0x36 0x80

5. 문제점, 그리고 해결

예제에 있는 ChangeUID로 간편하게 문제를 해결하고 싶었으나 계속 실패했다.

0x40 백도어가 자꾸 타임아웃이 나는 것.

알고보니 내 카드는 Gen2 카드라서 그냥 0번 섹터에 접근한 뒤 UID를 교체하기만 하면 되는 것이었다.

코드는 아래와 같다.

#include <SPI.h>
#include <MFRC522.h>

#define RST_PIN  9
#define SS_PIN   10

MFRC522 mfrc522(SS_PIN, RST_PIN);
MFRC522::MIFARE_Key key;

// 바꾸고 싶은 새 UID (4바이트)
byte newUid[4] = { 0xF5, 0x5F, 0x36, 0x80 };  // 예시

void setup() {
  Serial.begin(9600);
  while (!Serial) {}

  SPI.begin();
  mfrc522.PCD_Init();
  Serial.println(F("CUID 카드 Block 0에 직접 UID 쓰기 예제"));

  // 기본 키 FF..FF 세팅
  for (byte i = 0; i < 6; i++) {
    key.keyByte[i] = 0xFF;
  }
}

void loop() {
  // 새 카드 대기
  if (!mfrc522.PICC_IsNewCardPresent() || !mfrc522.PICC_ReadCardSerial()) {
    return;
  }

  Serial.print(F("현재 카드 UID: "));
  for (byte i = 0; i < mfrc522.uid.size; i++) {
    Serial.print(mfrc522.uid.uidByte[i], HEX);
    Serial.print(" ");
  }
  Serial.println();

  // === 1. 섹터 0 인증 (Block 0) ===
  byte block = 0;  // Block 0
  MFRC522::StatusCode status;

  status = mfrc522.PCD_Authenticate(
      MFRC522::PICC_CMD_MF_AUTH_KEY_A,
      block,
      &key,
      &(mfrc522.uid)
  );

  if (status != MFRC522::STATUS_OK) {
    Serial.print(F("인증 실패: "));
    Serial.println(mfrc522.GetStatusCodeName(status));
    goto HALT;
  }

  // === 2. 기존 Block 0 내용을 읽어서 manufacturer 부분 보존 ===
  byte block0[18];  // 16바이트 + 크기 정보
  byte size = sizeof(block0);
  status = mfrc522.MIFARE_Read(0, block0, &size);

  if (status != MFRC522::STATUS_OK) {
    Serial.print(F("Block 0 읽기 실패: "));
    Serial.println(mfrc522.GetStatusCodeName(status));
    goto HALT;
  }

  // block0[0..3] = 원래 UID
  // block0[4]    = BCC (UID 4바이트 XOR)
  // block0[5..15]= 제조사 데이터 등

  // === 3. 새 UID + BCC 계산해서 덮어쓰기 ===
  byte bcc = newUid[0] ^ newUid[1] ^ newUid[2] ^ newUid[3];

  block0[0] = newUid[0];
  block0[1] = newUid[1];
  block0[2] = newUid[2];
  block0[3] = newUid[3];
  block0[4] = bcc;
  // block0[5..15] 는 그대로 두면 제조사 데이터 유지

  status = mfrc522.MIFARE_Write(0, block0, 16);
  if (status != MFRC522::STATUS_OK) {
    Serial.print(F("Block 0 쓰기 실패: "));
    Serial.println(mfrc522.GetStatusCodeName(status));
    goto HALT;
  }

  Serial.println(F("새 UID 쓰기 완료. 카드를 떼었다가 다시 대보세요."));

HALT:
  mfrc522.PICC_HaltA();
  mfrc522.PCD_StopCrypto1();

  delay(1000);
} 

6. 후기

이번에 RFID 모듈을 어떻게 써야 하는지 제대로 알게 되었다.

검색을 해보면 카드키를 이용해 문을 여는 프로젝트트를 하던데, 그런 것도 쉽게 구현이 가능할 듯 하다.

다양하게 응용해 보고 싶다.

참고로, 해당 방법에도 카드가 인식이 되지 않는다면 주파수 문제(13.56MHz, 125KHz 등)일 가능성이 크다.

혹시 질문이 있다면 댓글 바란다.

댓글을 불러오는 중...