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

힘센캥거루
2025년 11월 26일
5
29

오늘은 아두이노로 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 등)일 가능성이 크다.

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

관련 글

아두이노 ESP32로 온습도 데이터 수집하기
아두이노 ESP32로 온습도 데이터 수집하기
오늘은 아두이노 ESP32로 온습도를 측정하고 데이터를 전송하는 와이파이 백엽상을 만들어볼 것이다.해당 내용은 학교에서 진행할 16+1 수업 원고를 바탕으로 작성한 글이다.1. 준비물준비물은 간단하다.ESP32, DHT-22, 전선 3개먼저 ESP32를 간단하게 설명하...
아두이노 D1 R2 사용기
아두이노 D1 R2 사용기
그냥 아두이노 uno로 하면 될 것을, 괜히 내장 와이파이 달린거 써보겠다고 wemos d1 r2를 쓰다가 고생을 꽤나 했다.이 글은 d1 r2와 같이 핀맵핑이 다른 아두이노를 사용하는 이들을 위한 글이다.1. IDE 셋팅아두이노의 종류가 다양해서 각 보드에 맞는 보드...
맥북에서 아두이노 Timed out waiting for packet header 해결 방법
맥북에서 아두이노 Timed out waiting for packet header 해결 방법
맥북에서 아두이노 Wemos D1 R2 연결시 Timed out 문제 해결하는 방법
아두이노와 스프레드시트 연동하기 - 코드 구성하기
아두이노와 스프레드시트 연동하기 - 코드 구성하기
지난 글에서는 아두이노와 스프레드시트 연동을 위한 시트 설정에 대해 알아보았다.이번 글에서는 아두이노 D1 보드에서 https 통신을 이용한 데이터 전송 방법에 대해 알아보자.1. 아두이노 D1 보드 라이브러리 설치하기아두이노 D1 보드를 이용하기 위해선 먼저 보드 라...
아두이노와 스프레드시트 연동하기 - 구글 시트 설정
아두이노와 스프레드시트 연동하기 - 구글 시트 설정
최근에 학생들과 아두이노를 이용해 학교 주변의 온도, 습도를 관측해보고 값을 분석해보기로 했다.&nbsp;아두이노에서 측정한 데이터를 저장하려니 SD카드가 필요했고, 데이터를 확인하기 위해서는 SD카드를 빼고 다시 끼우는 번거로움이 있었다.&nbsp;&nbsp;문득 데...

댓글을 불러오는 중...