mirror of
https://github.com/RfidResearchGroup/ChameleonUltra.git
synced 2026-05-12 11:22:59 -07:00
New cmd added.
This commit is contained in:
@@ -55,9 +55,10 @@ class ChameleonCLI:
|
||||
'help': "Device mode get/set"
|
||||
},
|
||||
'slot': {
|
||||
'set': new_uint(chameleon_cli_unit.HWSlotSet, "Set emulation tag slot activated."),
|
||||
'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"),
|
||||
'enable': new_uint(chameleon_cli_unit.HWSlotEnableSet, "Set emulation tag slot enable or disable"),
|
||||
'help': "Emulation tag slot.",
|
||||
},
|
||||
'help': "hardware controller",
|
||||
@@ -73,6 +74,12 @@ class ChameleonCLI:
|
||||
'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"),
|
||||
'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"
|
||||
},
|
||||
'help': "Mifare Classic mini/1/2/4, attack/read/write"
|
||||
},
|
||||
'help': "high frequency tag/reader",
|
||||
@@ -81,7 +88,8 @@ class ChameleonCLI:
|
||||
'em': {
|
||||
'read': new_uint(chameleon_cli_unit.LFEMRead, "Scan em410x tag and print id"),
|
||||
'write': new_uint(chameleon_cli_unit.LFEMWriteT55xx, "Write em410x id to t55xx"),
|
||||
'help': "EM410x read/write",
|
||||
'sim': new_uint(chameleon_cli_unit.LFEMSim, "Simulation a em410x id card."),
|
||||
'help': "EM410x read/write/emulator",
|
||||
},
|
||||
'help': "low frequency tag/reader",
|
||||
}
|
||||
|
||||
@@ -498,6 +498,112 @@ class HFMFWRBL(BaseMF1AuthOpera):
|
||||
print(f" - {colorama.Fore.RED}Write fail.{colorama.Style.RESET_ALL}")
|
||||
|
||||
|
||||
class HFMFDetectionEnable(DeviceRequiredUnit):
|
||||
|
||||
def args_parser(self) -> ArgumentParserNoExit or None:
|
||||
parser = ArgumentParserNoExit()
|
||||
parser.add_argument('-e', '--enable', type=int, required=True, choices=[1, 0],
|
||||
help="1 = enable, 0 = disable")
|
||||
return parser
|
||||
|
||||
# hf mf detection enable -e 1
|
||||
def on_exec(self, args: argparse.Namespace):
|
||||
enable = True if args.enable == 1 else False
|
||||
self.cmd_positive.set_mf1_detection_enable(enable)
|
||||
print(f" - Set mf1 detection { 'enable' if enable else 'disable'}.")
|
||||
|
||||
|
||||
class HFMFDetectionLogCount(DeviceRequiredUnit):
|
||||
|
||||
def args_parser(self) -> ArgumentParserNoExit or None:
|
||||
return None
|
||||
|
||||
# hf mf detection count
|
||||
def on_exec(self, args: argparse.Namespace):
|
||||
data_bytes = self.cmd_standard.get_mf1_detection_count().data
|
||||
count = int.from_bytes(data_bytes, "little", signed=False)
|
||||
print(f" - MF1 detection log count = {count}")
|
||||
|
||||
|
||||
class HFMFDetectionDecrypt(DeviceRequiredUnit):
|
||||
|
||||
detection_log_size = 18
|
||||
|
||||
def args_parser(self) -> ArgumentParserNoExit or None:
|
||||
return None
|
||||
|
||||
def decrypt_by_list(self, rs: list):
|
||||
"""
|
||||
从侦测日志列表中解密秘钥
|
||||
:param rs:
|
||||
:return:
|
||||
"""
|
||||
keys = []
|
||||
for i in range(len(rs)):
|
||||
item0 = rs[i]
|
||||
for j in range(i + 1, len(rs)):
|
||||
item1 = rs[j]
|
||||
cmd_base = f"{item0['uid']} {item0['nt']} {item0['nr']} {item0['ar']}"
|
||||
cmd_base += f" {item1['nt']} {item1['nr']} {item1['ar']}"
|
||||
cmd_recover = f"mfkey32v2.exe {cmd_base}"
|
||||
# print(cmd_recover)
|
||||
# Found Key: [e899c526c5cd]
|
||||
# subprocess.run(cmd_final, cwd=os.path.abspath("../bin/"), shell=True)
|
||||
process = self.sub_process(cmd_recover)
|
||||
# wait end
|
||||
process.wait_process()
|
||||
# get output
|
||||
output_str = process.get_output_sync()
|
||||
# print(output_str)
|
||||
sea_obj = re.search(r"([a-fA-F0-9]{12})", output_str, flags=re.MULTILINE)
|
||||
if sea_obj is not None:
|
||||
keys.append(sea_obj[1])
|
||||
|
||||
return keys
|
||||
|
||||
# hf mf detection decrypt
|
||||
def on_exec(self, args: argparse.Namespace):
|
||||
buffer = bytearray()
|
||||
index = 0
|
||||
count = int.from_bytes(self.cmd_standard.get_mf1_detection_count().data, "little", signed=False)
|
||||
if count == 0:
|
||||
print(" - No detection log to download")
|
||||
return
|
||||
print(f" - MF1 detection log count = {count}, start download", end="")
|
||||
while index < count:
|
||||
tmp = self.cmd_positive.get_mf1_detection_log(index).data
|
||||
recv_count = int(len(tmp) / HFMFDetectionDecrypt.detection_log_size)
|
||||
index += recv_count
|
||||
buffer.extend(tmp)
|
||||
print(f".", end="")
|
||||
print()
|
||||
print(f" - Download done ({len(buffer)}bytes), start parse and decrypt")
|
||||
|
||||
result_maps = chameleon_cstruct.parse_mf1_detection_result(buffer)
|
||||
for uid in result_maps.keys():
|
||||
print(f" - Detection log for uid [{uid.upper()}]")
|
||||
result_maps_for_uid = result_maps[uid]
|
||||
for block in result_maps_for_uid:
|
||||
print(f" > Block {block} detect log decrypting...")
|
||||
if 'A' in result_maps_for_uid[block]:
|
||||
# print(f" - A record: { result_maps[block]['A'] }")
|
||||
records = result_maps_for_uid[block]['A']
|
||||
if len(records) > 1:
|
||||
result_maps[uid][block]['A'] = self.decrypt_by_list(records)
|
||||
if 'B' in result_maps_for_uid[block]:
|
||||
# print(f" - B record: { result_maps[block]['B'] }")
|
||||
records = result_maps_for_uid[block]['B']
|
||||
if len(records) > 1:
|
||||
result_maps[uid][block]['B'] = self.decrypt_by_list(records)
|
||||
print(" > Result ---------------------------")
|
||||
for block in result_maps_for_uid.keys():
|
||||
if 'A' in result_maps_for_uid[block]:
|
||||
print(f" > Block {block}, A key result: {result_maps_for_uid[block]['A']}")
|
||||
if 'B' in result_maps_for_uid[block]:
|
||||
print(f" > Block {block}, B key result: {result_maps_for_uid[block]['B']}")
|
||||
return
|
||||
|
||||
|
||||
class LFEMRead(ReaderRequiredUint):
|
||||
|
||||
def args_parser(self) -> ArgumentParserNoExit or None:
|
||||
@@ -509,32 +615,69 @@ class LFEMRead(ReaderRequiredUint):
|
||||
print(f" - EM410x ID(10H): {colorama.Fore.GREEN}{id_hex}{colorama.Style.RESET_ALL}")
|
||||
|
||||
|
||||
class LFEMWriteT55xx(ReaderRequiredUint):
|
||||
def args_parser(self) -> ArgumentParserNoExit or None:
|
||||
parser = ArgumentParserNoExit()
|
||||
class LFEMCardRequiredUint(DeviceRequiredUnit):
|
||||
|
||||
@staticmethod
|
||||
def add_card_arg(parser: ArgumentParserNoExit):
|
||||
parser.add_argument("--id", type=str, required=True, help="EM410x tag id", metavar="hex")
|
||||
return parser
|
||||
|
||||
def before_exec(self, args: argparse.Namespace):
|
||||
if super(LFEMCardRequiredUint, self).before_exec(args):
|
||||
if not re.match(r"^[a-fA-F0-9]{10}$", args.id):
|
||||
raise ArgsParserError("ID must include 10 HEX symbols")
|
||||
return True
|
||||
return False
|
||||
|
||||
def args_parser(self) -> ArgumentParserNoExit or None:
|
||||
raise NotImplementedError("Please implement this")
|
||||
|
||||
def on_exec(self, args: argparse.Namespace):
|
||||
raise NotImplementedError("Please implement this")
|
||||
|
||||
|
||||
class LFEMWriteT55xx(LFEMCardRequiredUint, ReaderRequiredUint):
|
||||
|
||||
def args_parser(self) -> ArgumentParserNoExit or None:
|
||||
parser = ArgumentParserNoExit()
|
||||
return self.add_card_arg(parser)
|
||||
|
||||
def before_exec(self, args: argparse.Namespace):
|
||||
b1 = super(LFEMCardRequiredUint, self).before_exec(args)
|
||||
b2 = super(ReaderRequiredUint, self).before_exec(args)
|
||||
return b1 and b2
|
||||
|
||||
# lf em write --id 4400999559
|
||||
def on_exec(self, args: argparse.Namespace):
|
||||
id_hex = args.id
|
||||
if not re.match(r"^[a-fA-F0-9]{10}$", id_hex):
|
||||
raise ArgsParserError("ID must include 10 HEX symbols")
|
||||
id_bytes = bytearray.fromhex(id_hex)
|
||||
self.cmd_positive.write_em_410x_to_t55xx(id_bytes)
|
||||
print(f" - EM410x ID(10H): {id_hex} write done.")
|
||||
|
||||
|
||||
class HWSlotSet(DeviceRequiredUnit):
|
||||
class SlotIndexRequireUint(DeviceRequiredUnit):
|
||||
|
||||
def args_parser(self) -> ArgumentParserNoExit or None:
|
||||
parser = ArgumentParserNoExit()
|
||||
raise NotImplementedError()
|
||||
|
||||
def on_exec(self, args: argparse.Namespace):
|
||||
raise NotImplementedError()
|
||||
|
||||
@staticmethod
|
||||
def add_slot_args(parser: ArgumentParserNoExit):
|
||||
slot_choices = [1, 2, 3, 4, 5, 6, 7, 8]
|
||||
parser.add_argument('-s', "--slot", type=int, required=True,
|
||||
help="Slot index", metavar="number", choices=slot_choices)
|
||||
return parser
|
||||
|
||||
# hw slot set -s 1
|
||||
|
||||
class HWSlotSet(SlotIndexRequireUint):
|
||||
|
||||
def args_parser(self) -> ArgumentParserNoExit or None:
|
||||
parser = ArgumentParserNoExit()
|
||||
return self.add_slot_args(parser)
|
||||
|
||||
# hw slot change -s 1
|
||||
def on_exec(self, args: argparse.Namespace):
|
||||
slot_index = args.slot
|
||||
self.cmd_positive.set_slot_activated(slot_index)
|
||||
@@ -543,8 +686,8 @@ class HWSlotSet(DeviceRequiredUnit):
|
||||
|
||||
class TagTypeRequiredUint(DeviceRequiredUnit):
|
||||
|
||||
def args_parser(self) -> ArgumentParserNoExit or None:
|
||||
parser = ArgumentParserNoExit()
|
||||
@staticmethod
|
||||
def add_type_args(parser: ArgumentParserNoExit):
|
||||
type_choices = chameleon_cmd.TagSpecificType.list()
|
||||
help_str = ""
|
||||
for name, value in chameleon_cmd.TagSpecificType.__members__.items():
|
||||
@@ -555,11 +698,20 @@ class TagTypeRequiredUint(DeviceRequiredUnit):
|
||||
metavar="number", choices=type_choices)
|
||||
return parser
|
||||
|
||||
def args_parser(self) -> ArgumentParserNoExit or None:
|
||||
raise NotImplementedError()
|
||||
|
||||
def on_exec(self, args: argparse.Namespace):
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class HWSlotTagType(TagTypeRequiredUint):
|
||||
class HWSlotTagType(TagTypeRequiredUint, SlotIndexRequireUint):
|
||||
|
||||
def args_parser(self) -> ArgumentParserNoExit or None:
|
||||
parser = ArgumentParserNoExit()
|
||||
self.add_type_args(parser)
|
||||
self.add_slot_args(parser)
|
||||
return parser
|
||||
|
||||
# hw slot tagtype -t 2
|
||||
def on_exec(self, args: argparse.Namespace):
|
||||
@@ -568,13 +720,12 @@ class HWSlotTagType(TagTypeRequiredUint):
|
||||
print(f' - Set slot tag type success.')
|
||||
|
||||
|
||||
class HWSlotDataDefault(TagTypeRequiredUint):
|
||||
class HWSlotDataDefault(TagTypeRequiredUint, SlotIndexRequireUint):
|
||||
|
||||
def args_parser(self) -> ArgumentParserNoExit or None:
|
||||
parser = super(HWSlotDataDefault, self).args_parser()
|
||||
slot_choices = [1, 2, 3, 4, 5, 6, 7, 8]
|
||||
parser.add_argument('-s', "--slot", type=int, required=True,
|
||||
help="Slot index", metavar="number", choices=slot_choices)
|
||||
parser = ArgumentParserNoExit()
|
||||
self.add_type_args(parser)
|
||||
self.add_slot_args(parser)
|
||||
return parser
|
||||
|
||||
# hw slot init -s 1 -t 2
|
||||
@@ -583,3 +734,32 @@ class HWSlotDataDefault(TagTypeRequiredUint):
|
||||
slot_num = args.slot
|
||||
self.cmd_positive.set_slot_data_default(slot_num, tag_type)
|
||||
print(f' - Set slot tag data init success.')
|
||||
|
||||
|
||||
class HWSlotEnableSet(SlotIndexRequireUint):
|
||||
def args_parser(self) -> ArgumentParserNoExit or None:
|
||||
parser = ArgumentParserNoExit()
|
||||
self.add_slot_args(parser)
|
||||
parser.add_argument('-e', '--enable', type=int, required=True, help="1 is Enable or 0 Disable", choices=[0, 1])
|
||||
return parser
|
||||
|
||||
# hw slot enable -s 1 -e 0
|
||||
def on_exec(self, args: argparse.Namespace):
|
||||
slot_num = args.slot
|
||||
enable = args.enable
|
||||
self.cmd_positive.set_slot_enable(slot_num, enable)
|
||||
print(f' - Set slot {slot_num} {"enable" if enable else "disable"} success.')
|
||||
|
||||
|
||||
class LFEMSim(LFEMCardRequiredUint):
|
||||
|
||||
def args_parser(self) -> ArgumentParserNoExit or None:
|
||||
parser = ArgumentParserNoExit()
|
||||
return self.add_card_arg(parser)
|
||||
|
||||
# lf em sim --id 4545454545
|
||||
def on_exec(self, args: argparse.Namespace):
|
||||
id_hex = args.id
|
||||
id_bytes = bytearray.fromhex(id_hex)
|
||||
self.cmd_positive.set_em140x_sim_id(id_bytes)
|
||||
print(f' - Set em410x tag id success.')
|
||||
|
||||
@@ -8,6 +8,7 @@ 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_SCAN_14A_TAG = 2000
|
||||
DATA_CMD_MF1_SUPPORT_DETECT = 2001
|
||||
@@ -23,6 +24,11 @@ DATA_CMD_MF1_WRITE_ONE_BLOCK = 2009
|
||||
DATA_CMD_SCAN_EM410X_TAG = 3000
|
||||
DATA_CMD_WRITE_EM410X_TO_T5577 = 3001
|
||||
|
||||
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):
|
||||
@@ -238,20 +244,24 @@ class BaseChameleonCMD:
|
||||
data.append(slot_index - 1)
|
||||
return self.device.send_cmd_sync(DATA_CMD_SET_SLOT_ACTIVATED, 0x00, data)
|
||||
|
||||
def set_slot_tag_type(self, tag_type: TagSpecificType):
|
||||
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: 要设置的缺省标签类型
|
||||
@@ -264,6 +274,57 @@ class BaseChameleonCMD:
|
||||
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)
|
||||
|
||||
|
||||
class NegativeResponseError(Exception):
|
||||
"""
|
||||
@@ -348,8 +409,8 @@ class PositiveChameleonCMD(BaseChameleonCMD):
|
||||
self.check_status(ret.status, chameleon_status.Device.STATUS_DEVICE_SUCCESS)
|
||||
return ret
|
||||
|
||||
def set_slot_tag_type(self, tag_type: TagSpecificType):
|
||||
ret = super(PositiveChameleonCMD, self).set_slot_tag_type(tag_type)
|
||||
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
|
||||
|
||||
@@ -357,3 +418,27 @@ class PositiveChameleonCMD(BaseChameleonCMD):
|
||||
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
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -69,3 +69,63 @@ def parse_darkside_acquire_result(data: bytearray):
|
||||
'nr': bytes_to_u32(data[24: 28]),
|
||||
'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
|
||||
:param data: data
|
||||
:return:
|
||||
"""
|
||||
# 转换
|
||||
result_list = []
|
||||
pos = 0
|
||||
while pos < len(data):
|
||||
result_list.append({
|
||||
'block': data[0 + pos],
|
||||
'type': 0x60 + (data[1 + pos] & 0x01),
|
||||
'is_nested': True if data[1 + pos] >> 1 & 0x01 == 0x01 else False,
|
||||
'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(),
|
||||
})
|
||||
pos += 18
|
||||
|
||||
# 归类
|
||||
result_map = {}
|
||||
for item in result_list:
|
||||
uid = item['uid']
|
||||
if uid not in result_map:
|
||||
result_map[uid] = {}
|
||||
|
||||
block = item['block']
|
||||
if block not in result_map[uid]:
|
||||
result_map[uid][block] = {}
|
||||
|
||||
type_chr = 'A' if item['type'] == 0x60 else 'B'
|
||||
if type_chr not in result_map[uid][block]:
|
||||
result_map[uid][block][type_chr] = []
|
||||
|
||||
result_map[uid][block][type_chr].append(item)
|
||||
|
||||
return result_map
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user