Implement new Mifare Classic emulator configuration commands, improve CLI

Slot

Won't work like this

More randomness on nested auth (for hardnested recovery) and change default GCC location

Nobody will have GCC in this default location, /usr/bin/ will target way more users

Get enabled slots command

hw slot list show disabled slots

Improve python code quality

Show Mifare Classic emulator settings in hw slot list

Implement hf mf settings to change Mifare Classic emulator settings

Update
This commit is contained in:
Foxushka
2023-08-19 20:59:39 +03:00
parent de5aa050fd
commit 151f412490
11 changed files with 564 additions and 287 deletions
+14 -18
View File
@@ -1,5 +1,4 @@
import argparse
import os
import platform
import sys
import traceback
@@ -10,9 +9,6 @@ import colorama
import chameleon_cli_unit
import os
if os.name == 'posix':
import readline
ULTRA = r"""
╦ ╦╦ ╔╦╗╦═╗╔═╗
███████ ║ ║║ ║ ╠╦╝╠═╣
@@ -43,10 +39,7 @@ def new_uint(unit_clz, help_msg):
:param help_msg: unit usage
:return: a dict...
"""
return {
'unit': unit_clz,
'help': help_msg,
}
return {'unit': unit_clz, 'help': help_msg}
class ChameleonCLI:
@@ -60,7 +53,7 @@ class ChameleonCLI:
'connect': new_uint(chameleon_cli_unit.HWConnect, "Connect to chameleon by serial port"),
'chipid': {
'get': new_uint(chameleon_cli_unit.HWChipIdGet, "Get device chipset ID"),
'help': "Device chipsed ID get"
'help': "Device chipset ID get"
},
'address': {
'get': new_uint(chameleon_cli_unit.HWAddressGet, "Get device address (used with Bluetooth)"),
@@ -72,7 +65,7 @@ class ChameleonCLI:
'help': "Device mode get/set"
},
'slot': {
'info': new_uint(chameleon_cli_unit.HWSlotInfo, "Get information about slots"),
'list': new_uint(chameleon_cli_unit.HWSlotList, "Get information about slots"),
'change': new_uint(chameleon_cli_unit.HWSlotSet, "Set emulation tag slot activated."),
'type': new_uint(chameleon_cli_unit.HWSlotTagType, "Set emulation tag type"),
'init': new_uint(chameleon_cli_unit.HWSlotDataDefault, "Set emulation tag data to default"),
@@ -87,7 +80,8 @@ class ChameleonCLI:
'help': "Emulation tag slot.",
},
'version': new_uint(chameleon_cli_unit.HWVersion, "Get current device firmware version"),
'dfu': new_uint(chameleon_cli_unit.HWDFU, "Restart application to bootloader mode(Not yet implement dfu)."),
'dfu': new_uint(chameleon_cli_unit.HWDFU, "Restart application to bootloader mode(Not yet implement "
"dfu)."),
'settings': {
'animation': {
'get': new_uint(chameleon_cli_unit.HWSettingsAnimationGet, "Get current animation mode value"),
@@ -98,7 +92,8 @@ class ChameleonCLI:
'reset': new_uint(chameleon_cli_unit.HWSettingsReset, "Reset settings to default values"),
'help': "Chameleon settings management"
},
'factory_reset': new_uint(chameleon_cli_unit.HWFactoryReset, "Wipe all data and return to factory settings"),
'factory_reset': new_uint(chameleon_cli_unit.HWFactoryReset, "Wipe all data and return to factory "
"settings"),
'help': "hardware controller",
},
'hf': {
@@ -110,15 +105,16 @@ class ChameleonCLI:
'mf': {
'nested': new_uint(chameleon_cli_unit.HFMFNested, "Mifare Classic nested recover key"),
'darkside': new_uint(chameleon_cli_unit.HFMFDarkside, "Mifare Classic darkside recover key"),
'rdbl': new_uint(chameleon_cli_unit.HFMFRDBL, "MiFARE Classic read one block"),
'wrbl': new_uint(chameleon_cli_unit.HFMFWRBL, "MiFARE Classic write one block"),
'rdbl': new_uint(chameleon_cli_unit.HFMFRDBL, "Mifare Classic read one block"),
'wrbl': new_uint(chameleon_cli_unit.HFMFWRBL, "Mifare Classic write one block"),
'detection': {
'enable': new_uint(chameleon_cli_unit.HFMFDetectionEnable, "Detection enable"),
'count': new_uint(chameleon_cli_unit.HFMFDetectionLogCount, "Detection log count"),
'decrypt': new_uint(chameleon_cli_unit.HFMFDetectionDecrypt, "Download log and decrypt keys"),
'help': "Mifare Classic detection log"
},
'sim': new_uint(chameleon_cli_unit.HFMFSim, "Simulation a mifare classic card"),
'settings': new_uint(chameleon_cli_unit.HFMFSettings, "Settings of Mifare Classic emulator"),
'sim': new_uint(chameleon_cli_unit.HFMFSim, "Simulate a Mifare Classic card"),
'eload': new_uint(chameleon_cli_unit.HFMFELoad, "Load data to emulator memory"),
'help': "Mifare Classic mini/1/2/4, attack/read/write"
},
@@ -154,12 +150,10 @@ class ChameleonCLI:
"""
cmds = cmd_str.split(" ")
cmd_maps: dict or types.FunctionType = self.cmd_maps
cmd_end = ""
cmd_end_position = 0
for cmd in cmds:
if cmd in cmd_maps: # CMD found in map, we can continue find next
cmd_maps = cmd_maps[cmd]
cmd_end = cmd
cmd_end_position += len(cmd) + 1
else: # CMD not found
break
@@ -175,13 +169,15 @@ class ChameleonCLI:
while True:
# wait user input
status = f"{colorama.Fore.GREEN}USB" if self.device_com.isOpen() else f"{colorama.Fore.RED}Offline"
cmd_str = ""
try:
cmd_str = input(f"[{status}{colorama.Style.RESET_ALL}] chameleon --> ").strip()
except EOFError:
print("")
closing = True
if closing or cmd_str == "exit" or cmd_str == "quit" or cmd_str.startswith('q', 0) or cmd_str.startswith('e', 0):
if closing or cmd_str == "exit" or cmd_str == "quit" or cmd_str.startswith('q', 0) or cmd_str.startswith(
'e', 0):
print("Bye, thank you. ^.^ ")
self.device_com.close()
sys.exit(996)
File diff suppressed because it is too large Load Diff
+136 -62
View File
@@ -32,6 +32,8 @@ DATA_CMD_GET_SLOT_INFO = 1019
DATA_CMD_WIPE_FDS = 1020
DATA_CMD_GET_ENABLED_SLOTS = 1023
DATA_CMD_SCAN_14A_TAG = 2000
DATA_CMD_MF1_SUPPORT_DETECT = 2001
DATA_CMD_MF1_NT_LEVEL_DETECT = 2002
@@ -49,11 +51,22 @@ DATA_CMD_WRITE_EM410X_TO_T5577 = 3001
DATA_CMD_LOAD_MF1_BLOCK_DATA = 4000
DATA_CMD_SET_MF1_ANTI_COLLISION_RES = 4001
DATA_CMD_GET_MF1_EMULATOR_CONFIG = 4009
DATA_CMD_GET_MF1_GEN1A_MODE = 4010
DATA_CMD_SET_MF1_GEN1A_MODE = 4011
DATA_CMD_GET_MF1_GEN2_MODE = 4012
DATA_CMD_SET_MF1_GEN2_MODE = 4013
DATA_CMD_GET_MF1_USE_FIRST_BLOCK_COLL = 4014
DATA_CMD_SET_MF1_USE_FIRST_BLOCK_COLL = 4015
DATA_CMD_GET_MF1_WRITE_MODE = 4016
DATA_CMD_SET_MF1_WRITE_MODE = 4017
DATA_CMD_SET_EM410X_EMU_ID = 5000
DATA_CMD_SET_MF1_DETECTION_ENABLE = 5003
DATA_CMD_GET_MF1_DETECTION_COUNT = 5004
DATA_CMD_GET_MF1_DETECTION_RESULT = 5005
@enum.unique
class SlotNumber(enum.IntEnum):
SLOT_1 = 1,
@@ -66,7 +79,7 @@ class SlotNumber(enum.IntEnum):
SLOT_8 = 8,
@staticmethod
def to_fw(index: int): # can be int or SlotNumber
def to_fw(index: int): # can be int or SlotNumber
# SlotNumber() will raise error for us if index not in slot range
return SlotNumber(index).value - 1
@@ -78,13 +91,12 @@ class SlotNumber(enum.IntEnum):
@enum.unique
class TagSenseType(enum.IntEnum):
# 无场感应
TAG_SENSE_NO = 0,
# 低频125khz场感应
TAG_SENSE_LF = 1,
# 高频13.56mhz场感应
TAG_SENSE_HF = 2,
# Unknown
TAG_SENSE_NO = 0
# 125 kHz
TAG_SENSE_LF = 1
# 13.56 MHz
TAG_SENSE_HF = 2
@staticmethod
def list(exclude_unknown=True):
@@ -100,18 +112,19 @@ class TagSenseType(enum.IntEnum):
return "HF"
return "None"
@enum.unique
class TagSpecificType(enum.IntEnum):
# 特定的且必须存在的标志不存在的类型
# Empty slot
TAG_TYPE_UNKNOWN = 0
# 125khzID卡)系列
# 125 kHzIDcards
TAG_TYPE_EM410X = 1
# Mifare系列
# Mifare Classic
TAG_TYPE_MIFARE_Mini = 2
TAG_TYPE_MIFARE_1024 = 3
TAG_TYPE_MIFARE_2048 = 4
TAG_TYPE_MIFARE_4096 = 5
# NTAG系列
# NTAG
TAG_TYPE_NTAG_213 = 6
TAG_TYPE_NTAG_215 = 7
TAG_TYPE_NTAG_216 = 8
@@ -143,6 +156,32 @@ class TagSpecificType(enum.IntEnum):
return "Unknown"
@enum.unique
class MifareClassicWriteMode(enum.IntEnum):
# Normal write
NORMAL = 0
# Send NACK to write attempts
DEINED = 1
# Acknowledge writes, but don't remember contents
DECEIVE = 2
# Store data to RAM, but not to ROM
SHADOW = 3
@staticmethod
def list():
return list(map(int, MifareClassicWriteMode))
def __str__(self):
if self == MifareClassicWriteMode.NORMAL:
return "Normal"
elif self == MifareClassicWriteMode.DEINED:
return "Deined"
elif self == MifareClassicWriteMode.DECEIVE:
return "Deceive"
elif self == MifareClassicWriteMode.SHADOW:
return "Shadow"
return "None"
class BaseChameleonCMD:
"""
@@ -159,25 +198,25 @@ class BaseChameleonCMD:
"""
Get firmware version number(application)
"""
resp = self.device.send_cmd_sync(DATA_CMD_GET_APP_VERSION, 0x00, None)
resp = self.device.send_cmd_sync(DATA_CMD_GET_APP_VERSION, 0x00)
return int.from_bytes(resp.data, 'little')
def get_device_chip_id(self) -> str:
"""
Get device chip id
"""
resp = self.device.send_cmd_sync(DATA_CMD_GET_DEVICE_CHIP_ID, 0x00, None)
resp = self.device.send_cmd_sync(DATA_CMD_GET_DEVICE_CHIP_ID, 0x00)
return resp.data.hex()
def get_device_address(self) -> str:
"""
Get device address
"""
resp = self.device.send_cmd_sync(DATA_CMD_GET_DEVICE_ADDRESS, 0x00, None)
resp = self.device.send_cmd_sync(DATA_CMD_GET_DEVICE_ADDRESS, 0x00)
return resp.data[::-1].hex()
def get_git_version(self) -> str:
resp = self.device.send_cmd_sync(DATA_CMD_GET_GIT_VERSION, 0x00, None)
resp = self.device.send_cmd_sync(DATA_CMD_GET_GIT_VERSION, 0x00)
return resp.data.decode('utf-8')
def is_reader_device_mode(self) -> bool:
@@ -185,7 +224,7 @@ class BaseChameleonCMD:
Get device mode, reader or tag
:return: True is reader mode, else tag mode
"""
resp = self.device.send_cmd_sync(DATA_CMD_GET_DEVICE_MODE, 0x00, None)
resp = self.device.send_cmd_sync(DATA_CMD_GET_DEVICE_MODE, 0x00)
return True if resp.data[0] == 1 else False
def set_reader_device_mode(self, reader_mode: bool = True):
@@ -201,21 +240,21 @@ class BaseChameleonCMD:
扫描场内的14a标签
:return:
"""
return self.device.send_cmd_sync(DATA_CMD_SCAN_14A_TAG, 0x00, None)
return self.device.send_cmd_sync(DATA_CMD_SCAN_14A_TAG, 0x00)
def detect_mf1_support(self):
"""
检测是否是mifare classic标签
:return:
"""
return self.device.send_cmd_sync(DATA_CMD_MF1_SUPPORT_DETECT, 0x00, None)
return self.device.send_cmd_sync(DATA_CMD_MF1_SUPPORT_DETECT, 0x00)
def detect_mf1_nt_level(self):
"""
检测mifare classic的nt漏洞的等级
:return:
"""
return self.device.send_cmd_sync(DATA_CMD_MF1_NT_LEVEL_DETECT, 0x00, None)
return self.device.send_cmd_sync(DATA_CMD_MF1_NT_LEVEL_DETECT, 0x00)
def detect_darkside_support(self):
"""
@@ -315,7 +354,7 @@ class BaseChameleonCMD:
读取EM410X的卡号
:return:
"""
return self.device.send_cmd_sync(DATA_CMD_SCAN_EM410X_TAG, 0x00, None)
return self.device.send_cmd_sync(DATA_CMD_SCAN_EM410X_TAG, 0x00)
def write_em_410x_to_t55xx(self, id_bytes: bytearray):
"""
@@ -324,10 +363,7 @@ class BaseChameleonCMD:
:return:
"""
new_key = [0x20, 0x20, 0x66, 0x66]
old_keys = [
[0x51, 0x24, 0x36, 0x48],
[0x19, 0x92, 0x04, 0x27],
]
old_keys = [[0x51, 0x24, 0x36, 0x48], [0x19, 0x92, 0x04, 0x27]]
if len(id_bytes) != 5:
raise ValueError("The id bytes length must equal 5")
data = bytearray()
@@ -342,19 +378,19 @@ class BaseChameleonCMD:
Get slots info
:return:
"""
return self.device.send_cmd_sync(DATA_CMD_GET_SLOT_INFO, 0x00, None)
return self.device.send_cmd_sync(DATA_CMD_GET_SLOT_INFO, 0x00)
def get_active_slot(self):
"""
Get selected slot
:return:
"""
return self.device.send_cmd_sync(DATA_CMD_GET_ACTIVE_SLOT, 0x00, None)
return self.device.send_cmd_sync(DATA_CMD_GET_ACTIVE_SLOT, 0x00)
def set_slot_activated(self, slot_index: SlotNumber):
"""
设置当前激活使用的卡槽
:param slot_index: 卡槽索引,从 1 - 8(不是从0下标开始)
Set the card slot currently active for use
:param slot_index: Card slot index
:return:
"""
# SlotNumber() will raise error for us if slot_index not in slot range
@@ -428,7 +464,7 @@ class BaseChameleonCMD:
获取当前侦测记录的统计个数
:return:
"""
return self.device.send_cmd_sync(DATA_CMD_GET_MF1_DETECTION_COUNT, 0x00, None)
return self.device.send_cmd_sync(DATA_CMD_GET_MF1_DETECTION_COUNT, 0x00)
def get_mf1_detection_log(self, index: int):
"""
@@ -465,7 +501,7 @@ class BaseChameleonCMD:
data.extend(atqa)
data.extend(uid)
return self.device.send_cmd_sync(DATA_CMD_SET_MF1_ANTI_COLLISION_RES, 0X00, data)
def set_slot_tag_nick_name(self, slot: SlotNumber, sense_type: TagSenseType, name: bytes):
"""
设置MF1的模拟卡的防冲撞资源信息
@@ -479,26 +515,61 @@ class BaseChameleonCMD:
data.extend([SlotNumber.to_fw(slot), sense_type])
data.extend(name)
return self.device.send_cmd_sync(DATA_CMD_SET_SLOT_TAG_NICK, 0x00, data)
def get_slot_tag_nick_name(self, slot: SlotNumber, sense_type: TagSenseType):
"""
设置MF1的模拟卡的防冲撞资源信息
:param slot: 卡槽号码
:param sense_type: 场类型
:param name: 卡槽昵称
:return:
"""
# SlotNumber() will raise error for us if slot not in slot range
data = bytearray()
data.extend([SlotNumber.to_fw(slot), sense_type])
return self.device.send_cmd_sync(DATA_CMD_GET_SLOT_TAG_NICK, 0x00, data)
def get_mf1_emulator_settings(self):
"""
Get array of Mifare Classic emulators settings:
[0] - mf1_is_detection_enable (mfkey32)
[1] - mf1_is_gen1a_magic_mode
[2] - mf1_is_gen2_magic_mode
[3] - mf1_is_use_mf1_coll_res (use UID/BCC/SAK/ATQA from 0 block)
[4] - mf1_get_write_mode
:return:
"""
return self.device.send_cmd_sync(DATA_CMD_GET_MF1_EMULATOR_CONFIG, 0x00)
def set_mf1_gen1a_mode(self, enabled: bool):
"""
Set gen1a magic mode
"""
return self.device.send_cmd_sync(DATA_CMD_SET_MF1_GEN1A_MODE, 0x00, bytearray([1 if enabled else 0]))
def set_mf1_gen2_mode(self, enabled: bool):
"""
Set gen2 magic mode
"""
return self.device.send_cmd_sync(DATA_CMD_SET_MF1_GEN2_MODE, 0x00, bytearray([1 if enabled else 0]))
def set_mf1_block_anti_coll_mode(self, enabled: bool):
"""
Set 0 block anti-collision data
"""
return self.device.send_cmd_sync(DATA_CMD_SET_MF1_ANTI_COLLISION_RES, 0x00, bytearray([1 if enabled else 0]))
def set_mf1_write_mode(self, mode: int):
"""
Set write mode
"""
return self.device.send_cmd_sync(DATA_CMD_SET_MF1_WRITE_MODE, 0x00, bytearray([mode]))
def update_slot_data_config(self):
"""
更新卡槽的配置和数据到flash中。
:return:
"""
return self.device.send_cmd_sync(DATA_CMD_SLOT_DATA_CONFIG_SAVE, 0x00, None)
return self.device.send_cmd_sync(DATA_CMD_SLOT_DATA_CONFIG_SAVE, 0x00)
def enter_dfu_mode(self):
"""
@@ -506,19 +577,25 @@ class BaseChameleonCMD:
:return:
"""
return self.device.send_cmd_auto(DATA_CMD_ENTER_BOOTLOADER, 0x00, close=True)
def get_settings_animation(self):
"""
Get animation mode value
"""
return self.device.send_cmd_sync(DATA_CMD_GET_ANIMATION_MODE, 0x00, None)
return self.device.send_cmd_sync(DATA_CMD_GET_ANIMATION_MODE, 0x00)
def get_enabled_slots(self):
"""
Get animation mode value
"""
return self.device.send_cmd_sync(DATA_CMD_GET_ENABLED_SLOTS, 0x00)
def set_settings_animation(self, value: int):
"""
Set animation mode value
"""
return self.device.send_cmd_sync(DATA_CMD_SET_ANIMATION_MODE, 0x00, bytearray([value]))
def reset_settings(self):
"""
Reset settings stored in flash memory
@@ -530,7 +607,7 @@ class BaseChameleonCMD:
Store settings to flash memory
"""
return self.device.send_cmd_sync(DATA_CMD_SAVE_SETTINGS, 0x00)
def factory_reset(self):
"""
Reset to factory settings
@@ -578,8 +655,8 @@ class PositiveChameleonCMD(BaseChameleonCMD):
return ret
def acquire_nested(self, block_known, type_known, key_known, block_target, type_target):
ret = super(PositiveChameleonCMD, self).acquire_nested(
block_known, type_known, key_known, block_target, type_target)
ret = super(PositiveChameleonCMD, self).acquire_nested(block_known, type_known, key_known, block_target,
type_target)
self.check_status(ret.status, chameleon_status.Device.HF_TAG_OK)
return ret
@@ -590,10 +667,7 @@ class PositiveChameleonCMD(BaseChameleonCMD):
def auth_mf1_key(self, block, type_value, key):
ret = super(PositiveChameleonCMD, self).auth_mf1_key(block, type_value, key)
self.check_status(ret.status, [
chameleon_status.Device.HF_TAG_OK,
chameleon_status.Device.MF_ERRAUTH,
])
self.check_status(ret.status, [chameleon_status.Device.HF_TAG_OK, chameleon_status.Device.MF_ERRAUTH])
return ret
def read_mf1_block(self, block, type_value, key):
@@ -621,17 +695,17 @@ class PositiveChameleonCMD(BaseChameleonCMD):
self.check_status(ret.status, chameleon_status.Device.STATUS_DEVICE_SUCCESS)
return ret
def set_slot_tag_type(self, slot_index: int, tag_type: TagSpecificType):
def set_slot_tag_type(self, slot_index: SlotNumber, tag_type: TagSpecificType):
ret = super(PositiveChameleonCMD, self).set_slot_tag_type(slot_index, tag_type)
self.check_status(ret.status, chameleon_status.Device.STATUS_DEVICE_SUCCESS)
return ret
def set_slot_data_default(self, slot_index: int, tag_type: TagSpecificType):
def set_slot_data_default(self, slot_index: SlotNumber, tag_type: TagSpecificType):
ret = super(PositiveChameleonCMD, self).set_slot_data_default(slot_index, tag_type)
self.check_status(ret.status, chameleon_status.Device.STATUS_DEVICE_SUCCESS)
return ret
def set_slot_enable(self, slot_index: int, enable: bool):
def set_slot_enable(self, slot_index: SlotNumber, enable: bool):
ret = super(PositiveChameleonCMD, self).set_slot_enable(slot_index, enable)
self.check_status(ret.status, chameleon_status.Device.STATUS_DEVICE_SUCCESS)
return ret
@@ -656,17 +730,17 @@ class PositiveChameleonCMD(BaseChameleonCMD):
self.check_status(ret.status, chameleon_status.Device.STATUS_DEVICE_SUCCESS)
return ret
def set_mf1_anti_collision_res(self, sak: int, atqa: bytearray, uid: bytearray):
def set_mf1_anti_collision_res(self, sak: bytearray, atqa: bytearray, uid: bytearray):
ret = super(PositiveChameleonCMD, self).set_mf1_anti_collision_res(sak, atqa, uid)
self.check_status(ret.status, chameleon_status.Device.STATUS_DEVICE_SUCCESS)
return ret
def set_slot_tag_nick_name(self, slot: int, sense_type: int, name: bytes):
def set_slot_tag_nick_name(self, slot: SlotNumber, sense_type: TagSenseType, name: bytes):
ret = super(PositiveChameleonCMD, self).set_slot_tag_nick_name(slot, sense_type, name)
self.check_status(ret.status, chameleon_status.Device.STATUS_DEVICE_SUCCESS)
return ret
def get_slot_tag_nick_name(self, slot: int, sense_type: int):
def get_slot_tag_nick_name(self, slot: SlotNumber, sense_type: TagSenseType):
ret = super(PositiveChameleonCMD, self).get_slot_tag_nick_name(slot, sense_type)
self.check_status(ret.status, chameleon_status.Device.STATUS_DEVICE_SUCCESS)
return ret
@@ -679,12 +753,12 @@ if __name__ == '__main__':
cml = BaseChameleonCMD(dev)
ver = cml.get_firmware_version()
print(f"Firmware number of application: {ver}")
id = cml.get_device_chip_id()
print(f"Device chip id: {id}")
chip = cml.get_device_chip_id()
print(f"Device chip id: {chip}")
# disconnect
dev.close()
# nerver exit
while True: pass
# never exit
while True:
pass
+14 -27
View File
@@ -40,8 +40,6 @@ class ChameleonCom:
Chameleon device base class
Communication and Data frame implemented
"""
baudrate = 115200
data_frame_sof = 0x11
data_max_length = 512
@@ -49,7 +47,7 @@ class ChameleonCom:
"""
Create a chameleon device instance
"""
self.serial_instance: serial.Serial = None
self.serial_instance: serial.Serial | None = None
self.send_data_queue = queue.Queue()
self.wait_response_map = {}
self.event_closing = threading.Event()
@@ -72,7 +70,7 @@ class ChameleonCom:
error = None
try:
# open serial port
self.serial_instance = serial.Serial(port=port, baudrate=self.baudrate)
self.serial_instance = serial.Serial(port=port, baudrate=115200)
except Exception as e:
error = e
finally:
@@ -80,7 +78,7 @@ class ChameleonCom:
raise OpenFailException(error)
try:
self.serial_instance.dtr = 1 # must make dtr enable
except Exception as e:
except:
# not all serial support dtr, e.g. virtual serial over BLE
pass
self.serial_instance.timeout = 0 # noblock
@@ -89,9 +87,9 @@ class ChameleonCom:
self.wait_response_map.clear()
# Start a sub thread to process data
self.event_closing.clear()
threading.Thread(target=self.thread_data_receive, ).start()
threading.Thread(target=self.thread_data_transfer, ).start()
threading.Thread(target=self.thread_check_timeout, ).start()
threading.Thread(target=self.thread_data_receive).start()
threading.Thread(target=self.thread_data_transfer).start()
threading.Thread(target=self.thread_check_timeout).start()
return self
def check_open(self):
@@ -200,11 +198,8 @@ class ChameleonCom:
del self.wait_response_map[data_cmd]
fn_call(data_cmd, data_status, data_response)
else:
self.wait_response_map[data_cmd]['response'] = Response(
data_cmd,
data_status,
data_response
)
self.wait_response_map[data_cmd]['response'] = Response(data_cmd, data_status,
data_response)
else:
print(f"No task wait process: ${data_cmd}")
else:
@@ -232,13 +227,9 @@ class ChameleonCom:
task_close = task['close']
# register to wait map
if 'callback' in task and callable(task['callback']):
self.wait_response_map[task_cmd] = {
'callback': task['callback'] # The callback for this task
}
self.wait_response_map[task_cmd] = {'callback': task['callback']} # The callback for this task
else:
self.wait_response_map[task_cmd] = {
'response': None,
}
self.wait_response_map[task_cmd] = {'response': None}
# set start time
start_time = time.time()
self.wait_response_map[task_cmd]['start_time'] = start_time
@@ -279,7 +270,6 @@ class ChameleonCom:
:return: frame
"""
frame = bytearray()
lrc = 0x00
# sof and sof lrc byte
frame.append(self.data_frame_sof)
frame.append(self.lrc_calc(frame[0:1]))
@@ -295,7 +285,8 @@ class ChameleonCom:
frame.append(self.lrc_calc(frame))
return frame
def send_cmd_auto(self, cmd: int, status: int, data: bytearray = None, callback=None, timeout: int = 3, close: bool = False):
def send_cmd_auto(self, cmd: int, status: int, data: bytearray = None, callback=None, timeout: int = 3,
close: bool = False):
"""
Send cmd to device
:param timeout: wait response timeout
@@ -303,6 +294,7 @@ class ChameleonCom:
:param status: status(optional)
:param callback: call on response
:param data: bytes data
:param close: close connection after executing
:return:
"""
self.check_open()
@@ -311,12 +303,7 @@ class ChameleonCom:
del self.wait_response_map[cmd]
# make data frame
data_frame = self.make_data_frame_bytes(cmd, status, data)
task = {
'cmd': cmd,
'frame': data_frame,
'timeout': timeout,
'close': close,
}
task = {'cmd': cmd, 'frame': data_frame, 'timeout': timeout, 'close': close}
if callable(callback):
task['callback'] = callback
self.send_data_queue.put(task)
+7 -26
View File
@@ -22,7 +22,7 @@ def parse_14a_scan_tag_result(data: bytearray):
'uid_size': data[10],
'uid_hex': data[0:data[10]].hex(),
'sak_hex': hex(data[12]).lstrip('0x').rjust(2, '0'),
'atqa_hex': data[13:15].hex().upper(),
'atqa_hex': data[13:15].hex().upper()
}
@@ -49,7 +49,7 @@ def parse_nested_nt_acquire_group(data: bytearray):
group.append({
'nt': bytes_to_u32(data[i: i + 4]),
'nt_enc': bytes_to_u32(data[i + 4: i + 8]),
'par': data[i + 8],
'par': data[i + 8]
})
i += 9
return group
@@ -62,32 +62,15 @@ def parse_darkside_acquire_result(data: bytearray):
:return:
"""
return {
'uid': bytes_to_u32(data[0 : 4]),
'nt1': bytes_to_u32(data[4 : 8]),
'par': bytes_to_u32(data[8 : 16]),
'uid': bytes_to_u32(data[0: 4]),
'nt1': bytes_to_u32(data[4: 8]),
'par': bytes_to_u32(data[8: 16]),
'ks1': bytes_to_u32(data[16: 24]),
'nr': bytes_to_u32(data[24: 28]),
'ar': bytes_to_u32(data[28: 32]),
'ar': bytes_to_u32(data[28: 32])
}
"""
// 验证的基础信息
struct {
uint8_t block;
uint8_t is_keyb: 1;
uint8_t is_nested: 1;
// 空域占位置用的
uint8_t : 6;
} cmd;
// mfkey32必要参数
uint8_t uid[4];
uint8_t nt[4];
uint8_t nr[4];
uint8_t ar[4];
"""
def parse_mf1_detection_result(data: bytearray):
"""
From bytes parse detection param
@@ -105,7 +88,7 @@ def parse_mf1_detection_result(data: bytearray):
'uid': data[2 + pos: 6 + pos].hex(),
'nt': data[6 + pos: 10 + pos].hex(),
'nr': data[10 + pos: 14 + pos].hex(),
'ar': data[14 + pos: 18 + pos].hex(),
'ar': data[14 + pos: 18 + pos].hex()
})
pos += 18
@@ -127,5 +110,3 @@ def parse_mf1_detection_result(data: bytearray):
result_map[uid][block][type_chr].append(item)
return result_map
+48 -51
View File
@@ -1,6 +1,4 @@
class MetaDevice(type):
def __iter__(self):
for attr in dir(self):
if not attr.startswith("__"):
@@ -16,60 +14,59 @@ class MetaDevice(type):
class Device(metaclass=MetaDevice):
HF_TAG_OK = 0x00 # IC卡操作成功
HF_TAG_NO = 0x01 # 没有发现IC卡
HF_ERRSTAT = 0x02 # IC卡通信异常
HF_ERRCRC = 0x03 # IC卡通信校验异常
HF_COLLISION = 0x04 # IC卡冲突
HF_ERRBCC = 0x05 # IC卡BCC错误
MF_ERRAUTH = 0x06 # MF卡验证失败
HF_ERRPARITY = 0x07 # IC卡奇偶校验错误
HF_TAG_OK = 0x00 # IC卡操作成功
HF_TAG_NO = 0x01 # 没有发现IC卡
HF_ERRSTAT = 0x02 # IC卡通信异常
HF_ERRCRC = 0x03 # IC卡通信校验异常
HF_COLLISION = 0x04 # IC卡冲突
HF_ERRBCC = 0x05 # IC卡BCC错误
MF_ERRAUTH = 0x06 # MF卡验证失败
HF_ERRPARITY = 0x07 # IC卡奇偶校验错误
#
DARKSIDE_CANT_FIXED_NT = 0x20 # Darkside,无法固定随机数,这个情况可能出现在UID卡上
DARKSIDE_LUCK_AUTH_OK = 0x21 # Darkside,直接验证成功了,可能刚好密钥是空的
DARKSIDE_NACK_NO_SNED = 0x22 # Darkside,卡片不响应nack,可能是一张修复了nack逻辑漏洞的卡片
DARKSIDE_TAG_CHANGED = 0x23 # Darkside,在运行darkside的过程中出现了卡片切换,可能信号问题,或者真的是两张卡迅速切换了
NESTED_TAG_IS_STATIC = 0x24 # Nested,检测到卡片应答的随机数是固定的
NESTED_TAG_IS_HARD = 0x25 # Nested,检测到卡片应答的随机数是不可预测的
#
LF_TAG_OK = 0x40 # 低频卡的一些操作成功!
EM410X_TAG_NO_FOUND = 0x41 # 无法搜索到有效的EM410X标签
#
STATUS_PAR_ERR = 0x60 # BLE指令传递的参数错误,或者是调用某些函数传递的参数错误
STATUS_DEVIEC_MODE_ERROR = 0x66 # 当前设备所处的模式错误,无法调用对应的API
STATUS_INVALID_CMD = 0x67 # 无效的指令
STATUS_DEVICE_SUCCESS = 0x68 # 设备相关操作成功执行
STATUS_NOT_IMPLEMENTED = 0x69 # 调用了某些未实现的操作,属于开发者遗漏的错误
STATUS_FLASH_WRITE_FAIL = 0x70 # flash写入失败
STATUS_FLASH_READ_FAIL = 0x71 # flash读取失败
DARKSIDE_CANT_FIXED_NT = 0x20 # Darkside,无法固定随机数,这个情况可能出现在UID卡上
DARKSIDE_LUCK_AUTH_OK = 0x21 # Darkside,直接验证成功了,可能刚好密钥是空的
DARKSIDE_NACK_NO_SEND = 0x22 # Darkside,卡片不响应nack,可能是一张修复了nack逻辑漏洞的卡片
DARKSIDE_TAG_CHANGED = 0x23 # Darkside,在运行darkside的过程中出现了卡片切换,可能信号问题,或者真的是两张卡迅速切换了
NESTED_TAG_IS_STATIC = 0x24 # Nested,检测到卡片应答的随机数是固定的
NESTED_TAG_IS_HARD = 0x25 # Nested,检测到卡片应答的随机数是不可预测的
LF_TAG_OK = 0x40 # 低频卡的一些操作成功!
EM410X_TAG_NO_FOUND = 0x41 # 无法搜索到有效的EM410X标签
STATUS_PAR_ERR = 0x60 # BLE指令传递的参数错误,或者是调用某些函数传递的参数错误
STATUS_DEVICE_MODE_ERROR = 0x66 # 当前设备所处的模式错误,无法调用对应的API
STATUS_INVALID_CMD = 0x67 # 无效的指令
STATUS_DEVICE_SUCCESS = 0x68 # 设备相关操作成功执行
STATUS_NOT_IMPLEMENTED = 0x69 # 调用了某些未实现的操作,属于开发者遗漏的错误
STATUS_FLASH_WRITE_FAIL = 0x70 # flash写入失败
STATUS_FLASH_READ_FAIL = 0x71 # flash读取失败
message = {
Device.HF_TAG_OK : "HF tag operation succeeded",
Device.HF_TAG_NO : "HF tag no found or lost",
Device.HF_ERRSTAT : "HF tag status error",
Device.HF_ERRCRC : "HF tag data crc error",
Device.HF_COLLISION : "HF tag collision",
Device.HF_ERRBCC : "HF tag uid bcc error",
Device.MF_ERRAUTH : "HF tag auth fail",
Device.HF_ERRPARITY : "HF tag data parity error",
Device.HF_TAG_OK: "HF tag operation succeeded",
Device.HF_TAG_NO: "HF tag no found or lost",
Device.HF_ERRSTAT: "HF tag status error",
Device.HF_ERRCRC: "HF tag data crc error",
Device.HF_COLLISION: "HF tag collision",
Device.HF_ERRBCC: "HF tag uid bcc error",
Device.MF_ERRAUTH: "HF tag auth fail",
Device.HF_ERRPARITY: "HF tag data parity error",
Device.DARKSIDE_CANT_FIXED_NT : "Darkside Can't select a nt(PRNG is unpredictable)",
Device.DARKSIDE_LUCK_AUTH_OK : "Darkside try to recover a default key",
Device.DARKSIDE_NACK_NO_SNED : "Darkside can't make tag response nack(enc)",
Device.DARKSIDE_TAG_CHANGED : "Darkside running, can't change tag",
Device.NESTED_TAG_IS_STATIC : "StaticNested tag, not weak nested",
Device.NESTED_TAG_IS_HARD : "HardNested tag, not weak nested",
Device.DARKSIDE_CANT_FIXED_NT: "Darkside Can't select a nt(PRNG is unpredictable)",
Device.DARKSIDE_LUCK_AUTH_OK: "Darkside try to recover a default key",
Device.DARKSIDE_NACK_NO_SEND: "Darkside can't make tag response nack(enc)",
Device.DARKSIDE_TAG_CHANGED: "Darkside running, can't change tag",
Device.NESTED_TAG_IS_STATIC: "StaticNested tag, not weak nested",
Device.NESTED_TAG_IS_HARD: "HardNested tag, not weak nested",
Device.LF_TAG_OK : "LF tag operation succeeded",
Device.EM410X_TAG_NO_FOUND : "EM410x tag no found",
Device.LF_TAG_OK: "LF tag operation succeeded",
Device.EM410X_TAG_NO_FOUND: "EM410x tag no found",
Device.STATUS_PAR_ERR : "API request fail, param error",
Device.STATUS_DEVIEC_MODE_ERROR : "API request fail, device mode error",
Device.STATUS_INVALID_CMD : "API request fail, cmd invalid",
Device.STATUS_DEVICE_SUCCESS : "Device operation succeeded",
Device.STATUS_NOT_IMPLEMENTED : "Some api not implemented",
Device.STATUS_FLASH_WRITE_FAIL : "Flash write failed",
Device.STATUS_FLASH_READ_FAIL : "Flash read failed"
Device.STATUS_PAR_ERR: "API request fail, param error",
Device.STATUS_DEVICE_MODE_ERROR: "API request fail, device mode error",
Device.STATUS_INVALID_CMD: "API request fail, cmd invalid",
Device.STATUS_DEVICE_SUCCESS: "Device operation succeeded",
Device.STATUS_NOT_IMPLEMENTED: "Some api not implemented",
Device.STATUS_FLASH_WRITE_FAIL: "Flash write failed",
Device.STATUS_FLASH_READ_FAIL: "Flash read failed"
}