mirror of
https://github.com/RfidResearchGroup/ChameleonUltra.git
synced 2026-05-12 11:22:59 -07:00
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:
@@ -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
@@ -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
|
||||
# 125khz(ID卡)系列
|
||||
# 125 kHz(ID)cards
|
||||
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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user