Files
ChameleonUltra/software/script/chameleon_cmd.py
2023-03-22 11:29:34 +08:00

572 lines
20 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import enum
import chameleon_com
import chameleon_status
DATA_CMD_GET_APP_VERSION = 1000
DATA_CMD_CHANGE_MODE = 1001
DATA_CMD_GET_DEVICE_MODE = 1002
DATA_CMD_SET_SLOT_ACTIVATED = 1003
DATA_CMD_SET_SLOT_TAG_TYPE = 1004
DATA_CMD_SET_SLOT_DATA_DEFAULT = 1005
DATA_CMD_SET_SLOT_ENABLE = 1006
DATA_CMD_SET_SLOT_TAG_NICK = 1007
DATA_CMD_GET_SLOT_TAG_NICK = 1008
DATA_CMD_SLOT_DATA_CONFIG_SAVE = 1009
DATA_CMD_ENTER_BOOTLOADER = 1010
DATA_CMD_GET_DEVICE_CHIP_ID = 1011
DATA_CMD_SCAN_14A_TAG = 2000
DATA_CMD_MF1_SUPPORT_DETECT = 2001
DATA_CMD_MF1_NT_LEVEL_DETECT = 2002
DATA_CMD_MF1_DARKSIDE_DETECT = 2003
DATA_CMD_MF1_DARKSIDE_ACQUIRE = 2004
DATA_CMD_MF1_NT_DIST_DETECT = 2005
DATA_CMD_MF1_NESTED_ACQUIRE = 2006
DATA_CMD_MF1_CHECK_ONE_KEY_BLOCK = 2007
DATA_CMD_MF1_READ_ONE_BLOCK = 2008
DATA_CMD_MF1_WRITE_ONE_BLOCK = 2009
DATA_CMD_SCAN_EM410X_TAG = 3000
DATA_CMD_WRITE_EM410X_TO_T5577 = 3001
DATA_CMD_LOAD_MF1_BLOCK_DATA = 4000
DATA_CMD_SET_MF1_ANTI_COLLISION_RES = 4001
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 TagSenseType(enum.IntEnum):
# 无场感应
TAG_SENSE_NO = 0,
# 低频125khz场感应
TAG_SENSE_LF = 1,
# 高频13.56mhz场感应
TAG_SENSE_HF = 2,
@enum.unique
class TagSpecificType(enum.IntEnum):
# 特定的且必须存在的标志不存在的类型
TAG_TYPE_UNKNOWN = 0
# 125khzID卡系列
TAG_TYPE_EM410X = 1
# Mifare系列
TAG_TYPE_MIFARE_Mini = 2
TAG_TYPE_MIFARE_1024 = 3
TAG_TYPE_MIFARE_2048 = 4
TAG_TYPE_MIFARE_4096 = 5
# NTAG系列
TAG_TYPE_NTAG_213 = 6
TAG_TYPE_NTAG_215 = 7
TAG_TYPE_NTAG_216 = 8
@staticmethod
def list(exclude_unknown=True):
enum_list = list(map(int, TagSpecificType))
if exclude_unknown:
enum_list.remove(TagSpecificType.TAG_TYPE_UNKNOWN)
return enum_list
class BaseChameleonCMD:
"""
Chameleon cmd function
"""
def __init__(self, chameleon: chameleon_com.ChameleonCom):
"""
:param chameleon: chameleon instance, @see chameleon_device.Chameleon
"""
self.device = chameleon
def get_firmware_version(self) -> int:
"""
Get firmware version number(application)
"""
resp = self.device.send_cmd_sync(DATA_CMD_GET_APP_VERSION, 0x00, None)
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)
return resp.data.hex()
def is_reader_device_mode(self) -> bool:
"""
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)
return True if resp.data[0] == 1 else False
def set_reader_device_mode(self, reader_mode: bool = True):
"""
Change device mode, reader or tag
:param reader_mode: True if reader mode, False if tag mode.
:return:
"""
return self.device.send_cmd_sync(DATA_CMD_CHANGE_MODE, 0x00, 0x0001 if reader_mode else 0x0000)
def scan_tag_14a(self):
"""
扫描场内的14a标签
:return:
"""
return self.device.send_cmd_sync(DATA_CMD_SCAN_14A_TAG, 0x00, None)
def detect_mf1_support(self):
"""
检测是否是mifare classic标签
:return:
"""
return self.device.send_cmd_sync(DATA_CMD_MF1_SUPPORT_DETECT, 0x00, None)
def detect_mf1_nt_level(self):
"""
检测mifare classic的nt漏洞的等级
:return:
"""
return self.device.send_cmd_sync(DATA_CMD_MF1_NT_LEVEL_DETECT, 0x00, None)
def detect_darkside_support(self):
"""
检测卡片是否易受mifare classic darkside攻击
:return:
"""
return self.device.send_cmd_sync(DATA_CMD_MF1_DARKSIDE_DETECT, 0x00, None, timeout=20)
def detect_nt_distance(self, block_known, type_known, key_known):
"""
检测卡片的随机数距离
:return:
"""
data = bytearray()
data.append(type_known)
data.append(block_known)
data.extend(key_known)
return self.device.send_cmd_sync(DATA_CMD_MF1_NT_DIST_DETECT, 0x00, data)
def acquire_nested(self, block_known, type_known, key_known, block_target, type_target):
"""
采集Nested解密需要的关键NT参数
:return:
"""
data = bytearray()
data.append(type_known)
data.append(block_known)
data.extend(key_known)
data.append(type_target)
data.append(block_target)
return self.device.send_cmd_sync(DATA_CMD_MF1_NESTED_ACQUIRE, 0x00, data)
def acquire_darkside(self, block_target, type_target, first_recover: int or bool, sync_max):
"""
采集Darkside解密需要的关键参数
:param block_target:
:param type_target:
:param first_recover:
:param sync_max:
:return:
"""
data = bytearray()
data.append(type_target)
data.append(block_target)
if isinstance(first_recover, bool):
first_recover = 0x01 if first_recover else 0x00
data.append(first_recover)
data.append(sync_max)
return self.device.send_cmd_sync(DATA_CMD_MF1_DARKSIDE_ACQUIRE, 0x00, data, timeout=sync_max + 5)
def auth_mf1_key(self, block, type_value, key):
"""
验证mf1秘钥只验证单个扇区的指定类型的秘钥
:param block:
:param type_value:
:param key:
:return:
"""
data = bytearray()
data.append(type_value)
data.append(block)
data.extend(key)
return self.device.send_cmd_sync(DATA_CMD_MF1_CHECK_ONE_KEY_BLOCK, 0x00, data)
def read_mf1_block(self, block, type_value, key):
"""
读取mf1单块
:param block:
:param type_value:
:param key:
:return:
"""
data = bytearray()
data.append(type_value)
data.append(block)
data.extend(key)
return self.device.send_cmd_sync(DATA_CMD_MF1_READ_ONE_BLOCK, 0x00, data)
def write_mf1_block(self, block, type_value, key, block_data):
"""
写入mf1单块
:param block:
:param type_value:
:param key:
:param block_data:
:return:
"""
data = bytearray()
data.append(type_value)
data.append(block)
data.extend(key)
data.extend(block_data)
return self.device.send_cmd_sync(DATA_CMD_MF1_WRITE_ONE_BLOCK, 0x00, data)
def read_em_410x(self):
"""
读取EM410X的卡号
:return:
"""
return self.device.send_cmd_sync(DATA_CMD_SCAN_EM410X_TAG, 0x00, None)
def write_em_410x_to_t55xx(self, id_bytes: bytearray):
"""
写入EM410X卡号到T55XX中
:param id_bytes: ID卡号
:return:
"""
new_key = [0x20, 0x20, 0x66, 0x66]
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()
data.extend(id_bytes)
data.extend(new_key)
for key in old_keys:
data.extend(key)
return self.device.send_cmd_sync(DATA_CMD_WRITE_EM410X_TO_T5577, 0x00, data)
def set_slot_activated(self, slot_index):
"""
设置当前激活使用的卡槽
:param slot_index: 卡槽索引,从 1 - 8不是从0下标开始
:return:
"""
if slot_index < 1 or slot_index > 8:
raise ValueError("The slot index range error(1-8)")
data = bytearray()
data.append(slot_index - 1)
return self.device.send_cmd_sync(DATA_CMD_SET_SLOT_ACTIVATED, 0x00, data)
def set_slot_tag_type(self, slot_index: int, tag_type: TagSpecificType):
"""
设置当前卡槽的模拟卡的标签类型
注意此操作并不会更改flash中的数据flash中的数据的变动仅在下次保存时更新
:param slot_index: 卡槽号码
:param tag_type: 标签类型
:return:
"""
if slot_index < 1 or slot_index > 8:
raise ValueError("The slot index range error(1-8)")
data = bytearray()
data.append(slot_index - 1)
data.append(tag_type)
return self.device.send_cmd_sync(DATA_CMD_SET_SLOT_TAG_TYPE, 0x00, data)
def set_slot_data_default(self, slot_index: int, tag_type: TagSpecificType):
"""
设置指定卡槽的模拟卡的数据为缺省数据
注意此API会将flash中的数据一并进行设置
:param slot_index: 卡槽号码
:param tag_type: 要设置的缺省标签类型
:return:
"""
if slot_index < 1 or slot_index > 8:
raise ValueError("The slot index range error(1-8)")
data = bytearray()
data.append(slot_index - 1)
data.append(tag_type)
return self.device.send_cmd_sync(DATA_CMD_SET_SLOT_DATA_DEFAULT, 0x00, data)
def set_slot_enable(self, slot_index: int, enable: bool):
"""
设置指定的卡槽是否使能
:param slot_index: 卡槽号码
:param enable: 是否使能
:return:
"""
if slot_index < 1 or slot_index > 8:
raise ValueError("The slot index range error(1-8)")
data = bytearray()
data.append(slot_index - 1)
data.append(0x01 if enable else 0x00)
return self.device.send_cmd_sync(DATA_CMD_SET_SLOT_ENABLE, 0X00, data)
def set_em140x_sim_id(self, id_bytes: bytearray):
"""
设置EM410x模拟的卡号
:param id_bytes: 卡号的字节
:return:
"""
if len(id_bytes) != 5:
raise ValueError("The id bytes length must equal 5")
return self.device.send_cmd_sync(DATA_CMD_SET_EM410X_EMU_ID, 0x00, id_bytes)
def set_mf1_detection_enable(self, enable: bool):
"""
设置是否使能当前卡槽的侦测
:param enable: 是否使能
:return:
"""
data = bytearray()
data.append(0x01 if enable else 0x00)
return self.device.send_cmd_sync(DATA_CMD_SET_MF1_DETECTION_ENABLE, 0x00, data)
def get_mf1_detection_count(self):
"""
获取当前侦测记录的统计个数
:return:
"""
return self.device.send_cmd_sync(DATA_CMD_GET_MF1_DETECTION_COUNT, 0x00, None)
def get_mf1_detection_log(self, index: int):
"""
从指定的index位置开始获取侦测日志
:param index: 开始索引
:return:
"""
data = bytearray()
data.extend(index.to_bytes(4, "big", signed=False))
return self.device.send_cmd_sync(DATA_CMD_GET_MF1_DETECTION_RESULT, 0x00, data)
def set_mf1_block_data(self, block_start: int, block_data: bytearray):
"""
设置MF1的模拟卡的块数据
:param block_start: 开始设置块数据的位置,包含此位置
:param block_data: 要设置的块数据的字节缓冲区,可包含多个块数据,自动从 block_start 递增
:return:
"""
data = bytearray()
data.append(block_start & 0xFF)
data.extend(block_data)
return self.device.send_cmd_sync(DATA_CMD_LOAD_MF1_BLOCK_DATA, 0x00, data)
def set_mf1_anti_collision_res(self, sak: bytearray, atqa: bytearray, uid: bytearray):
"""
设置MF1的模拟卡的防冲撞资源信息
:param sak: sak字节
:param atqa: atqa数组
:param uid: 卡号数组
:return:
"""
data = bytearray()
data.extend(sak)
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: int, sense_type: int, name: str):
"""
设置MF1的模拟卡的防冲撞资源信息
:param slot: 卡槽号码
:param sense_type: 场类型
:param name: 卡槽昵称
:return:
"""
data = bytearray()
data.extend([slot, sense_type])
data.extend(name.encode(encoding="gbk"))
return self.device.send_cmd_sync(DATA_CMD_SET_SLOT_TAG_NICK, 0x00, data)
def get_slot_tag_nick_name(self, slot: int, sense_type: int):
"""
设置MF1的模拟卡的防冲撞资源信息
:param slot: 卡槽号码
:param sense_type: 场类型
:param name: 卡槽昵称
:return:
"""
data = bytearray()
data.extend([slot, sense_type])
return self.device.send_cmd_sync(DATA_CMD_GET_SLOT_TAG_NICK, 0x00, data)
def update_slot_data_config(self):
"""
更新卡槽的配置和数据到flash中。
:return:
"""
return self.device.send_cmd_sync(DATA_CMD_SLOT_DATA_CONFIG_SAVE, 0x00, None)
def enter_dfu_mode(self):
"""
重启进入DFU模式(bootloader)
:return:
"""
return self.device.send_cmd_auto(DATA_CMD_ENTER_BOOTLOADER, 0x00, None)
class NegativeResponseError(Exception):
"""
Not positive response
"""
class PositiveChameleonCMD(BaseChameleonCMD):
"""
子类重写基础指令交互实现类,针对每个指令进行单独封装结果处理
如果结果是成功状态,那么就返回对应的数据,否则直接抛出异常
"""
@staticmethod
def check_status(status_ret, status_except):
"""
检查状态码,如果在接受为成功的
:param status_ret: 执行指令之后返回的状态码
:param status_except: 可以认为是执行成功的状态码
:return:
"""
if isinstance(status_except, int):
status_except = [status_except]
if status_ret not in status_except:
if status_ret in chameleon_status.Device and status_ret in chameleon_status.message:
raise NegativeResponseError(chameleon_status.message[status_ret])
else:
raise NegativeResponseError(f"Not positive response and unknown status {status_ret}")
return
def scan_tag_14a(self):
ret = super(PositiveChameleonCMD, self).scan_tag_14a()
self.check_status(ret.status, chameleon_status.Device.HF_TAG_OK)
return ret
def detect_nt_distance(self, block_known, type_known, key_known):
ret = super(PositiveChameleonCMD, self).detect_nt_distance(block_known, type_known, key_known)
self.check_status(ret.status, chameleon_status.Device.HF_TAG_OK)
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)
self.check_status(ret.status, chameleon_status.Device.HF_TAG_OK)
return ret
def acquire_darkside(self, block_target, type_target, first_recover: int or bool, sync_max):
ret = super(PositiveChameleonCMD, self).acquire_darkside(block_target, type_target, first_recover, sync_max)
self.check_status(ret.status, chameleon_status.Device.HF_TAG_OK)
return ret
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,
])
return ret
def read_mf1_block(self, block, type_value, key):
ret = super(PositiveChameleonCMD, self).read_mf1_block(block, type_value, key)
self.check_status(ret.status, chameleon_status.Device.HF_TAG_OK)
return ret
def write_mf1_block(self, block, type_value, key, block_data):
ret = super(PositiveChameleonCMD, self).write_mf1_block(block, type_value, key, block_data)
self.check_status(ret.status, chameleon_status.Device.HF_TAG_OK)
return ret
def read_em_410x(self):
ret = super(PositiveChameleonCMD, self).read_em_410x()
self.check_status(ret.status, chameleon_status.Device.LF_TAG_OK)
return ret
def write_em_410x_to_t55xx(self, id_bytes: bytearray):
ret = super(PositiveChameleonCMD, self).write_em_410x_to_t55xx(id_bytes)
self.check_status(ret.status, chameleon_status.Device.LF_TAG_OK)
return ret
def set_slot_activated(self, slot_index):
ret = super(PositiveChameleonCMD, self).set_slot_activated(slot_index)
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):
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):
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):
ret = super(PositiveChameleonCMD, self).set_slot_enable(slot_index, enable)
self.check_status(ret.status, chameleon_status.Device.STATUS_DEVICE_SUCCESS)
return ret
def set_em140x_sim_id(self, id_bytes: bytearray):
ret = super(PositiveChameleonCMD, self).set_em140x_sim_id(id_bytes)
self.check_status(ret.status, chameleon_status.Device.STATUS_DEVICE_SUCCESS)
return ret
def set_mf1_detection_enable(self, enable: bool):
ret = super(PositiveChameleonCMD, self).set_mf1_detection_enable(enable)
self.check_status(ret.status, chameleon_status.Device.STATUS_DEVICE_SUCCESS)
return ret
def get_mf1_detection_log(self, index: int):
ret = super(PositiveChameleonCMD, self).get_mf1_detection_log(index)
self.check_status(ret.status, chameleon_status.Device.STATUS_DEVICE_SUCCESS)
return ret
def set_mf1_block_data(self, block_start: int, data: bytearray):
ret = super(PositiveChameleonCMD, self).set_mf1_block_data(block_start, data)
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):
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: str):
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):
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
if __name__ == '__main__':
# connect to chameleon
dev = chameleon_com.ChameleonCom()
dev.open("com19")
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}")
# disconnect
dev.close()
# nerver exit
while True: pass