還記得那個時候大一剛開學沒多久,某天路過籃球場的時候,發現地板上有張學生證。於是我停下了腳步,低頭一看,這個學號是…4113…053,好像是我們系的欸… 10X…。這個號碼、這張臉,這不是我室友嗎? 所以我就幫他撿回去了。但如果我沒有發現這張學生證,那他就會變成在開學一個月內就把學生證丟掉的奇葩。雖然事實就是這樣沒錯。 那現在如果學生證丟掉了怎麼辦呢?歡迎來找 Each 或是我,我們會以學校售價少五塊賣給你。就在今天我們找到了複製 UID 的方法。

背景知識

經過一番測試,我們推論學校的讀卡機只會讀 UID。只要裡面的 Sector 不要是 ???,在符合權限的情況下門都會開,不管你拿的是不是學生證正本。而學校的學生證是用 MIFARE Classic 1K 這種型號。它的一個 Sector 有四個 block,裡面存著

  • UID 唯一識別碼
  • BCC
  • 製造商資料

具體長這樣子。第一行前面四組是 UID,後面接著的是檢查位元,實作上就是把 UID 每組都做 XOR。

Block 0: he er is id bc 08 04 00 62 63 64 65 66 67 68 69
Block 1: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 2: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 3: FF FF FF FF FF FF FF 07 80 69 FF FF FF FF FF FF

後面的部分全部都是補0,一直持續到 Block 63。

Block 4: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 5: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 6: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Block 7: FF FF FF FF FF FF FF 07 80 69 FF FF FF FF FF FF

已經知道結構了那要怎麼複製呢?只要把 UID 的地方換成自己卡片的 UID,找張白卡寫進去就好了!!

手機

我們找到了這個專案,專門複製這種卡片,還可以自由的寫入UUID。還有工具可以算出 BBC 是多少方便改寫,超方便的,但一切的前提是你得有張白卡。以下是複製的步驟:

  1. 點擊 READ TAG ,把 extended-std.keysstd.keys 這兩個打勾,那是用來解密的字典。
  2. 接下來會進到 dump editor ,記住你的 UID,前面有說他在哪個位置。
  3. 回到主選單,找到 TOOLS 裡面的 BCC Calculator。輸入你的 UID,記住算出來的值。
  4. EDIT DUMP FILE 改寫 sector 的內容,存檔出去。
  5. WRITE TAG

這樣就可以複製出一張卡片了。

Flipper Zero

在講怎麼用之前,你得先有一個Flipper Zero。Flipper Zero有好幾個方法可以玩這個功能。

NFC Maker + NFC Magic

NFC Maker 裡用 Text Note 製作。內容要寫什麼無所謂,但是 Tag type 要做成 MIFARE Classic 1K UID 4 。接著輸入你的 UID,Flipper 會幫你做出一個 NFC tag。接下來要把這個 Tag 寫進白卡中。找到 NFC Magic,把要被寫入的卡片放上去,一直按 ok,到最後就會看到你的小海豚跟你說 Success。

模改 NFC 檔案內容 + NFC Magic

  • 學生證
    前面有提到,學校的讀卡機只看 UID。意思就是只要把 UID 跟 檢查位元都改成對的就結束了。以下是自動化程式:
import sys

def calculate_bcc(uid_bytes):
    """計算 Block Check Character (BCC) 使用 XOR"""
    bcc = 0
    for b in uid_bytes:
        bcc ^= b
    return bcc

def generate_nfc(uid_hex):
    # 清理輸入字串並轉換為位元組
    uid_hex = uid_hex.replace(" ", "").upper()
    if len(uid_hex) != 8:
        raise ValueError("UID 必須是精確的 4 個位元組 (8 個十六進位字元)。")

    uid_bytes = bytes.fromhex(uid_hex)
    bcc = calculate_bcc(uid_bytes)

    # 格式化 UID 以符合輸出格式 (例如 "xx xx xx xx")
    uid_str_spaced = " ".join([f"{b:02X}" for b in uid_bytes])

    # 組合 Block 0: UID (4 bytes) + BCC (1 byte) + 製造商資料 (11 bytes)
    block_0 = f"{uid_str_spaced} {bcc:02X} 04 00 62 63 64 65 66 67 68 69"

    # 生成所有 64 個區塊
    blocks = []
    for i in range(64):
        if i == 0:
            blocks.append(f"Block {i}: {block_0}")
        elif (i + 1) % 4 == 0:
            # 每個 Sector 的最後一個區塊為 Sector Trailer
            blocks.append(f"Block {i}: FF FF FF FF FF FF FF 07 80 69 FF FF FF FF FF FF")
        else:
            # 空白的資料區塊
            blocks.append(f"Block {i}: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00")

    # 組合完整檔案內容
    header = [
        "Filetype: Flipper NFC device",
        "Version: 4",
        "Device type: Mifare Classic",
        f"UID: {uid_str_spaced}",
        "ATQA: 00 04",
        "SAK: 08",
        "Mifare Classic type: 1K",
        "Data format version: 2",
    ]

    return "\n".join(header + blocks)

if __name__ == "__main__":
    if len(sys.argv) != 2:
        print("用法: python3 generate_nfc.py <UID>")
        print("範例: python3 generate_nfc.py 'xx xx xx xx'")
        sys.exit(1)

    uid_input = sys.argv[1]

    try:
        nfc_data = generate_nfc(uid_input)
        filename = f"mifare_{uid_input.replace(' ', '').upper()}.nfc"

        with open(filename, "w", encoding="utf-8") as f:
            f.write(nfc_data)

        print(f"執行成功。檔案已儲存為: {filename}")

    except Exception as e:
        print(f"錯誤: {e}")

在終端機輸入

python3 <your_file_name>.py "YOUR_UID"
  • Instagram、Blog 電子名片
import sys
import random

def generate_random_uid():
    """產生符合 NTAG 規範的 7-byte UID (0x04 開頭) 與對應的 BCC"""
    uid = [0x04] + [random.randint(0, 255) for _ in range(6)]
    
    # 0x88 是 Cascade Tag (CT)
    bcc0 = 0x88 ^ uid[0] ^ uid[1] ^ uid[2]
    bcc1 = uid[3] ^ uid[4] ^ uid[5] ^ uid[6]
    return uid, bcc0, bcc1

def encode_url_to_ndef(url):
    """將 URL 編碼為 NDEF URI 格式的位元組陣列"""
    prefixes = {
        "http://www.": 0x01,
        "https://www.": 0x02,
        "http://": 0x03,
        "https://": 0x04,
        "tel:": 0x05,
        "mailto:": 0x06,
    }
    
    prefix_code = 0x00
    url_body = url
    for prefix, code in prefixes.items():
        if url.startswith(prefix):
            prefix_code = code
            url_body = url[len(prefix):]
            break
            
    url_bytes = url_body.encode('utf-8')
    payload_len = len(url_bytes) + 1 # 1 byte for prefix code
    
    if payload_len > 254:
         raise ValueError("URL 過長,無法存入標準 Short Record NDEF。")
    
    # NDEF Record: [D1] [01] [Payload Len] [55='U'] [Prefix Code] [URL]
    ndef_record = bytearray([0xD1, 0x01, payload_len, 0x55, prefix_code]) + url_bytes
    
    # TLV Header: [03=NDEF Message] [NDEF Len] [NDEF Record] [FE=Terminator]
    tlv_msg = bytearray([0x03, len(ndef_record)]) + ndef_record + bytearray([0xFE])
    
    # 填充至 4 的倍數以符合 Page 大小
    while len(tlv_msg) % 4 != 0:
        tlv_msg.append(0x00)
        
    return tlv_msg

def generate_nfc_file(url):
    uid, bcc0, bcc1 = generate_random_uid()
    uid_hex_str = " ".join([f"{b:02X}" for b in uid])
    
    header = [
        "Filetype: Flipper NFC device",
        "Version: 4",
        "Device type: NTAG/Ultralight",
        f"UID: {uid_hex_str}",
        "ATQA: 00 44",
        "SAK: 00",
        "Data format version: 2",
        "NTAG/Ultralight type: NTAG215",
        "Signature: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00",
        "Mifare version: 00 04 04 02 01 00 11 03",
        "Counter 0: 0",
        "Tearing 0: 00",
        "Counter 1: 0",
        "Tearing 1: 00",
        "Counter 2: 0",
        "Tearing 2: 00",
        "Pages total: 135",
        "Pages read: 135"
    ]

    pages = []
    
    # 區塊 0-2: UID, BCC, 靜態鎖定與內部位元組
    pages.append(f"Page 0: {uid[0]:02X} {uid[1]:02X} {uid[2]:02X} {bcc0:02X}")
    pages.append(f"Page 1: {uid[3]:02X} {uid[4]:02X} {uid[5]:02X} {uid[6]:02X}")
    pages.append(f"Page 2: {bcc1:02X} 48 00 00")
    
    # 區塊 3: Capability Container (CC)
    # E1: NDEF Magic Number, 10: Version 1.0, 3E: 496 bytes data area
    pages.append("Page 3: E1 10 3E 00")
    
    # 區塊 4-129: 使用者資料區 (User Memory)
    tlv_data = encode_url_to_ndef(url)
    user_pages = [tlv_data[i:i+4] for i in range(0, len(tlv_data), 4)]
    
    current_page = 4
    for p_data in user_pages:
        hex_str = " ".join([f"{b:02X}" for b in p_data])
        pages.append(f"Page {current_page}: {hex_str}")
        current_page += 1
        
    for i in range(current_page, 130):
        pages.append(f"Page {i}: 00 00 00 00")
        
    # 區塊 130-134: 動態鎖定、CFG 與密碼配置
    pages.append("Page 130: 00 00 00 BD")
    pages.append("Page 131: 04 00 00 FF")
    pages.append("Page 132: 00 05 00 00")
    pages.append("Page 133: FF FF FF FF")
    pages.append("Page 134: 00 00 00 00")
    
    footer = [
        "Failed authentication attempts: 0"
    ]
    
    return "\n".join(header + pages + footer)

if __name__ == "__main__":
    if len(sys.argv) != 2:
        print("用法: python3 generate_ndef_url.py <URL>")
        print("範例: python3 generate_ndef_url.py 'https://windson.cc'")
        sys.exit(1)

    url_input = sys.argv[1]
    
    try:
        nfc_data = generate_nfc_file(url_input)
        
        # 從 URL 萃取主網域做為檔名
        domain = url_input.split("://")[-1].split("/")[0].replace(".", "_")
        filename = f"url_{domain}.nfc"
        
        with open(filename, "w", encoding="utf-8") as f:
            f.write(nfc_data)
            
        print(f"邏輯執行完畢。檔案已儲存為: {filename}")
        
    except Exception as e:
        print(f"錯誤: {e}")

在終端機輸入

python3 <YOUR_FILE_NAME> "YOUR_URL"

可用門地圖

最後,我們做了一張地圖,把到目前為止可以打開的門都標了上去。目前樣本數不夠,只能知道應數系館、二村、舊男宿、雲平樓以及圖書館的管制。如果有多會慢慢新增。


這篇文章如果你看不懂、又怕學生證不見,或是你需要一張電子名片,用非常快的方式讓大家加到你IG。歡迎來找我們,我們會在你面前花五分鐘複製一張,然後收你比學校還要便宜五塊錢的價格。

學生證丟了怎麼辦?

作者

Windson

發布日期

2026 - 03 - 19

版權

CC BY-NC-SA 4.0