Skip to content

Instantly share code, notes, and snippets.

@notxesh
Created August 9, 2025 05:25
Show Gist options
  • Select an option

  • Save notxesh/2e6a362d934e380ffc9de94329011c39 to your computer and use it in GitHub Desktop.

Select an option

Save notxesh/2e6a362d934e380ffc9de94329011c39 to your computer and use it in GitHub Desktop.
UFR Zero XL UID Cloning
#!/usr/bin/env python3
import os
import time
from ctypes import *
from enum import Enum
"""
Quick and dirty script to log card UIDs
Implemented for the UFR Zero XL https://webshop.d-logic.com/ufr-zero-xl.html
Read range is pretty bad, have to be practically touching the card to get a read
~10cm max for fobs while cards can be pushed to around 15-20
Useful for when access control systems are operating in UID only or CSN only mode.
Requires https://github.com/nfc-rfid-reader-sdk/ufr-lib in the same directory
If running on a pi, will need to change the library lib used to aarch64 / x86 etc. This is defined in the UIDReader Class "mySO"
"""
DL_OK = 0x00
class ErrCodes(Enum):
UFR_OK = 0x00
UFR_COMMUNICATION_ERROR = 0x01
UFR_CHKSUM_ERROR = 0x02
UFR_READING_ERROR = 0x03
UFR_WRITING_ERROR = 0x04
UFR_BUFFER_OVERFLOW = 0x05
UFR_MAX_ADDRESS_EXCEEDED = 0x06
UFR_MAX_KEY_INDEX_EXCEEDED = 0x07
UFR_NO_CARD = 0x08
UFR_COMMAND_NOT_SUPPORTED = 0x09
UFR_FORBIDEN_DIRECT_WRITE_IN_SECTOR_TRAILER = 0x0A
UFR_ADDRESSED_BLOCK_IS_NOT_SECTOR_TRAILER = 0x0B
UFR_WRONG_ADDRESS_MODE = 0x0C
UFR_WRONG_ACCESS_BITS_VALUES = 0x0D
UFR_AUTH_ERROR = 0x0E
UFR_PARAMETERS_ERROR = 0x0F
UFR_MAX_SIZE_EXCEEDED = 0x10
UFR_WRITE_VERIFICATION_ERROR = 0x70
UFR_BUFFER_SIZE_EXCEEDED = 0x71
UFR_VALUE_BLOCK_INVALID = 0x72
UFR_VALUE_BLOCK_ADDR_INVALID = 0x73
UFR_VALUE_BLOCK_MANIPULATION_ERROR = 0x74
UFR_WRONG_UI_MODE = 0x75
UFR_KEYS_LOCKED = 0x76
UFR_KEYS_UNLOCKED = 0x77
UFR_WRONG_PASSWORD = 0x78
UFR_CAN_NOT_LOCK_DEVICE = 0x79
UFR_CAN_NOT_UNLOCK_DEVICE = 0x7A
UFR_DEVICE_EEPROM_BUSY = 0x7B
UFR_RTC_SET_ERROR = 0x7C
UFR_COMMUNICATION_BREAK = 0x50
UFR_NO_MEMORY_ERROR = 0x51
UFR_CAN_NOT_OPEN_READER = 0x52
UFR_READER_NOT_SUPPORTED = 0x53
UFR_READER_OPENING_ERROR = 0x54
UFR_READER_PORT_NOT_OPENED = 0x55
UFR_CANT_CLOSE_READER_PORT = 0x56
UFR_FT_STATUS_ERROR_1 = 0xA0
UFR_FT_STATUS_ERROR_2 = 0xA1
UFR_FT_STATUS_ERROR_3 = 0xA2
UFR_FT_STATUS_ERROR_4 = 0xA3
UFR_FT_STATUS_ERROR_5 = 0xA4
UFR_FT_STATUS_ERROR_6 = 0xA5
UFR_FT_STATUS_ERROR_7 = 0xA6
UFR_FT_STATUS_ERROR_8 = 0xA7
UFR_FT_STATUS_ERROR_9 = 0xA8
MAX_UFR_STATUS = 0xFFFFFFFF
class CardTypes(Enum):
MIFARE_ULTRALIGHT = 0x01
MIFARE_ULTRALIGHT_EV1_11 = 0x02
MIFARE_ULTRALIGHT_EV1_21 = 0x03
MIFARE_ULTRALIGHT_C = 0x04
NTAG_203 = 0x05
NTAG_210 = 0x06
NTAG_212 = 0x07
NTAG_213 = 0x08
NTAG_215 = 0x09
NTAG_216 = 0x0A
MIFARE_MINI = 0x20
MIFARE_CLASSIC_1K = 0x21
MIFARE_CLASSIC_4K = 0x22
MIFARE_PLUS_S_2K = 0x23
MIFARE_PLUS_S_4K = 0x24
MIFARE_PLUS_X_2K = 0x25
MIFARE_PLUS_X_4K = 0x26
MIFARE_DESFIRE = 0x27
MIFARE_DESFIRE_EV1_2K = 0x28
MIFARE_DESFIRE_EV1_4K = 0x29
MIFARE_DESFIRE_EV1_8K = 0x2A
class UIDReader:
def __init__(self, sleep:int = 0.01):
self.sleep = sleep
self.__CONN = False
self.__readerOn = False
self.__functionOn = False
self.__dlogicCardType = c_int()
#self.mySO = cdll.LoadLibrary(os.getcwd()+'//ufr-lib//linux//x86//libuFCoder-x86.so')
self.mySO = cdll.LoadLibrary(os.getcwd()+'//ufr-lib//linux//x86_64//libuFCoder-x86_64.so')
self.logFile = open(f"{time.strftime("%Y%m%d_%H-%M")}_log.csv", 'w')
self.logFile.write('TIME, Card Type, uid size, uid\n')
# Start the cloning
self.loop()
def __getReaderOn(self):
return self.__readerOn
def __setReaderOn(self,value):
self.__readerOn = value
def __getFunctionOn(self):
return self.__functionOn
def __setFunctionOn(self,value):
self.__functionOn = value
FunctionOn = property(fget = __getFunctionOn,fset = __setFunctionOn)
ReaderOn = property(fget = __getReaderOn,fset = __setReaderOn)
def loop(self):
while True:
self.connect()
time.sleep(self.sleep)
def connect(self):
if self.FunctionOn : return
readerType = c_int32()
cardType = c_uint8()
cardUIDSize = c_uint8()
cardUID = (c_ubyte * 9)()
c = str()
try:
self._ReaderOn = True
if self.__CONN != True:
fResult = self.mySO.ReaderOpen()
if fResult == DL_OK:
print(f"CONNECTED: {hex(fResult)} - {ErrCodes(fResult).name}")
self.__CONN = True
else:
print(f"NOT CONNECTED: {hex(fResult)} - {ErrCodes(fResult).name}")
self.__CONN = False
if self.__CONN:
fResult = self.mySO.GetReaderType(byref(readerType))
if fResult == DL_OK:
fResult = self.mySO.GetDlogicCardType(byref(self.__dlogicCardType))
if fResult == DL_OK:
fResult = self.mySO.GetCardIdEx(byref(cardType),cardUID,byref(cardUIDSize))
if fResult == DL_OK:
for n in range(cardUIDSize.value):
c += '%0.2x' % cardUID[n]
cardType = CardTypes(self.__dlogicCardType.value).name
uidSize = hex(cardUIDSize.value)
logMsg = f"{time.strftime('%X %x %Z')}, {cardType}, {uidSize}, {c.upper()}\n"
self.logFile.write(logMsg)
print(logMsg[:-1])
else:
self.__CONN = False
self.mySO.ReaderClose
finally:
self.ReaderOn = False
if __name__ == '__main__':
UIDReader()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment