Created
August 9, 2025 05:25
-
-
Save notxesh/2e6a362d934e380ffc9de94329011c39 to your computer and use it in GitHub Desktop.
UFR Zero XL UID Cloning
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/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