mirror of
https://github.com/RfidResearchGroup/ChameleonUltra.git
synced 2026-05-12 11:22:59 -07:00
Merge branch 'main' into fix-argcomplete
This commit is contained in:
@@ -42,7 +42,8 @@ class ChameleonCLI:
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.completer = chameleon_utils.CustomNestedCompleter.from_nested_dict(chameleon_cli_unit.root_commands)
|
||||
self.completer = chameleon_utils.CustomNestedCompleter.from_nested_dict(
|
||||
chameleon_cli_unit.root_commands)
|
||||
self.session = prompt_toolkit.PromptSession(completer=self.completer,
|
||||
history=FileHistory(pathlib.Path.home() / ".chameleon_history"))
|
||||
|
||||
@@ -103,12 +104,14 @@ class ChameleonCLI:
|
||||
else:
|
||||
# wait user input
|
||||
try:
|
||||
cmd_str = self.session.prompt(ANSI(self.get_prompt())).strip()
|
||||
cmd_str = self.session.prompt(
|
||||
ANSI(self.get_prompt())).strip()
|
||||
except EOFError:
|
||||
closing = True
|
||||
except KeyboardInterrupt:
|
||||
closing = True
|
||||
cmd_strs = cmd_str.replace("\r\n", "\n").replace("\r", "\n").split("\n")
|
||||
cmd_strs = cmd_str.replace(
|
||||
"\r\n", "\n").replace("\r", "\n").split("\n")
|
||||
cmd_str = cmd_strs.pop(0)
|
||||
|
||||
if closing or cmd_str in ["exit", "quit", "q", "e"]:
|
||||
@@ -129,18 +132,21 @@ class ChameleonCLI:
|
||||
print("".ljust(18, "-") + "".ljust(10) + "".ljust(30, "-"))
|
||||
for cmd_name, cmd_node in chameleon_cli_unit.root_commands.items():
|
||||
cmd_title = f"{colorama.Fore.GREEN}{cmd_name}{colorama.Style.RESET_ALL}"
|
||||
help_line = (f" - {cmd_title}".ljust(37)) + f"[ {cmd_node.help_text} ]"
|
||||
help_line = (f" - {cmd_title}".ljust(37)
|
||||
) + f"[ {cmd_node.help_text} ]"
|
||||
print(help_line)
|
||||
continue
|
||||
|
||||
tree_node, arg_list = self.get_cmd_node(chameleon_cli_unit.root_commands[root_cmd], argv[1:])
|
||||
tree_node, arg_list = self.get_cmd_node(
|
||||
chameleon_cli_unit.root_commands[root_cmd], argv[1:])
|
||||
|
||||
if not tree_node.cls:
|
||||
# Found tree node is a group without an implementation, print children
|
||||
print("".ljust(18, "-") + "".ljust(10) + "".ljust(30, "-"))
|
||||
for child in tree_node.children:
|
||||
cmd_title = f"{colorama.Fore.GREEN}{child.name}{colorama.Style.RESET_ALL}"
|
||||
help_line = (f" - {cmd_title}".ljust(37)) + f"[ {child.help_text} ]"
|
||||
help_line = (f" - {cmd_title}".ljust(37)
|
||||
) + f"[ {child.help_text} ]"
|
||||
print(help_line)
|
||||
continue
|
||||
|
||||
@@ -170,7 +176,8 @@ class ChameleonCLI:
|
||||
except (chameleon_utils.UnexpectedResponseError, chameleon_utils.ArgsParserError) as e:
|
||||
print(f"{colorama.Fore.RED}{str(e)}{colorama.Style.RESET_ALL}")
|
||||
except Exception:
|
||||
print(f"CLI exception: {colorama.Fore.RED}{traceback.format_exc()}{colorama.Style.RESET_ALL}")
|
||||
print(
|
||||
f"CLI exception: {colorama.Fore.RED}{traceback.format_exc()}{colorama.Style.RESET_ALL}")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
@@ -158,8 +158,10 @@ hw_mode = hw.subgroup('mode', 'Device mode get/set')
|
||||
hw_slot = hw.subgroup('slot', 'Emulation tag slot.')
|
||||
hw_slot_nick = hw_slot.subgroup('nick', 'Get/Set tag nick name for slot')
|
||||
hw_settings = hw.subgroup('settings', 'Chameleon settings management')
|
||||
hw_settings_animation = hw_settings.subgroup('animation', 'Manage wake-up and sleep animation modes')
|
||||
hw_settings_button_press = hw_settings.subgroup('btnpress', 'Manage button press function')
|
||||
hw_settings_animation = hw_settings.subgroup(
|
||||
'animation', 'Manage wake-up and sleep animation modes')
|
||||
hw_settings_button_press = hw_settings.subgroup(
|
||||
'btnpress', 'Manage button press function')
|
||||
|
||||
hf = CLITree('hf', 'high frequency tag/reader')
|
||||
hf_14a = hf.subgroup('14a', 'ISO14443-a tag read/write/info...')
|
||||
@@ -169,7 +171,8 @@ hf_mf_detection = hf.subgroup(
|
||||
|
||||
lf = CLITree('lf', 'low frequency tag/reader')
|
||||
lf_em = lf.subgroup('em', 'EM410x read/write/emulator')
|
||||
lf_em_sim = lf_em.subgroup('sim', 'Manage EM410x emulation data for selected slot')
|
||||
lf_em_sim = lf_em.subgroup(
|
||||
'sim', 'Manage EM410x emulation data for selected slot')
|
||||
|
||||
root_commands: dict[str, CLITree] = {
|
||||
'hw': hw,
|
||||
@@ -194,7 +197,8 @@ class HWConnect(BaseCLIUnit):
|
||||
platform_name = uname().release
|
||||
if 'Microsoft' in platform_name:
|
||||
path = os.environ["PATH"].split(os.pathsep)
|
||||
path.append("/mnt/c/Windows/System32/WindowsPowerShell/v1.0/")
|
||||
path.append(
|
||||
"/mnt/c/Windows/System32/WindowsPowerShell/v1.0/")
|
||||
for prefix in path:
|
||||
fn = os.path.join(prefix, "powershell.exe")
|
||||
if not os.path.isdir(fn) and os.access(fn, os.X_OK):
|
||||
@@ -221,7 +225,8 @@ class HWConnect(BaseCLIUnit):
|
||||
args.port = port.device
|
||||
break
|
||||
if args.port is None: # If no chameleon was found, exit
|
||||
print("Chameleon not found, please connect the device or try connecting manually with the -p flag.")
|
||||
print(
|
||||
"Chameleon not found, please connect the device or try connecting manually with the -p flag.")
|
||||
return
|
||||
self.device_com.open(args.port)
|
||||
print(" { Chameleon connected } ")
|
||||
@@ -253,7 +258,8 @@ class HWModeGet(DeviceRequiredUnit):
|
||||
pass
|
||||
|
||||
def on_exec(self, args: argparse.Namespace):
|
||||
print(f"- Device Mode ( Tag {'Reader' if self.cmd.is_reader_device_mode() else 'Emulator'} )")
|
||||
print(
|
||||
f"- Device Mode ( Tag {'Reader' if self.cmd.is_reader_device_mode() else 'Emulator'} )")
|
||||
|
||||
|
||||
@hw_chipid.command('get', 'Get device chipset ID')
|
||||
@@ -351,7 +357,8 @@ class HFMFNested(ReaderRequiredUnit):
|
||||
help="The block where the key of the card is known")
|
||||
parser.add_argument('--type-known', type=str, required=True, choices=type_choices,
|
||||
help="The key type of the tag")
|
||||
parser.add_argument('--key-known', type=str, required=True, metavar="hex", help="tag sector key")
|
||||
parser.add_argument('--key-known', type=str,
|
||||
required=True, metavar="hex", help="tag sector key")
|
||||
parser.add_argument('--block-target', type=int, metavar="decimal",
|
||||
help="The key of the target block to recover")
|
||||
parser.add_argument('--type-target', type=str, choices=type_choices,
|
||||
@@ -370,10 +377,13 @@ class HFMFNested(ReaderRequiredUnit):
|
||||
:return:
|
||||
"""
|
||||
# acquire
|
||||
dist_resp = self.cmd.detect_nt_distance(block_known, type_known, key_known)
|
||||
nt_resp = self.cmd.acquire_nested(block_known, type_known, key_known, block_target, type_target)
|
||||
dist_resp = self.cmd.detect_nt_distance(
|
||||
block_known, type_known, key_known)
|
||||
nt_resp = self.cmd.acquire_nested(
|
||||
block_known, type_known, key_known, block_target, type_target)
|
||||
# parse
|
||||
dist_obj = chameleon_cstruct.parse_nt_distance_detect_result(dist_resp.data)
|
||||
dist_obj = chameleon_cstruct.parse_nt_distance_detect_result(
|
||||
dist_resp.data)
|
||||
nt_obj = chameleon_cstruct.parse_nested_nt_acquire_group(nt_resp.data)
|
||||
# create cmd
|
||||
cmd_param = f"{dist_obj['uid']} {dist_obj['dist']}"
|
||||
@@ -405,7 +415,8 @@ class HFMFNested(ReaderRequiredUnit):
|
||||
print(f" - [{len(key_list)} candidate keys found ]")
|
||||
for key in key_list:
|
||||
key_bytes = bytearray.fromhex(key)
|
||||
ret = self.cmd.auth_mf1_key(block_target, type_target, key_bytes)
|
||||
ret = self.cmd.auth_mf1_key(
|
||||
block_target, type_target, key_bytes)
|
||||
if ret.status == chameleon_status.Device.HF_TAG_OK:
|
||||
return key
|
||||
else:
|
||||
@@ -429,8 +440,10 @@ class HFMFNested(ReaderRequiredUnit):
|
||||
type_target = args.type_target
|
||||
if block_target is not None and type_target is not None:
|
||||
type_target = 0x60 if type_target == 'A' or type_target == 'a' else 0x61
|
||||
print(f" - {colorama.Fore.CYAN}Nested recover one key running...{colorama.Style.RESET_ALL}")
|
||||
key = self.recover_a_key(block_known, type_known, key_known, block_target, type_target)
|
||||
print(
|
||||
f" - {colorama.Fore.CYAN}Nested recover one key running...{colorama.Style.RESET_ALL}")
|
||||
key = self.recover_a_key(
|
||||
block_known, type_known, key_known, block_target, type_target)
|
||||
if key is None:
|
||||
print("No keys found, you can retry recover.")
|
||||
else:
|
||||
@@ -439,7 +452,8 @@ class HFMFNested(ReaderRequiredUnit):
|
||||
print("Please input block_target and type_target")
|
||||
self.args_parser().print_help()
|
||||
else:
|
||||
raise NotImplementedError("hf mf nested recover all key not implement.")
|
||||
raise NotImplementedError(
|
||||
"hf mf nested recover all key not implement.")
|
||||
|
||||
return
|
||||
|
||||
@@ -464,9 +478,11 @@ class HFMFDarkside(ReaderRequiredUnit):
|
||||
first_recover = True
|
||||
retry_count = 0
|
||||
while retry_count < 0xFF:
|
||||
darkside_resp = self.cmd.acquire_darkside(block_target, type_target, first_recover, 15)
|
||||
darkside_resp = self.cmd.acquire_darkside(
|
||||
block_target, type_target, first_recover, 15)
|
||||
first_recover = False # not first run.
|
||||
darkside_obj = chameleon_cstruct.parse_darkside_acquire_result(darkside_resp.data)
|
||||
darkside_obj = chameleon_cstruct.parse_darkside_acquire_result(
|
||||
darkside_resp.data)
|
||||
self.darkside_list.append(darkside_obj)
|
||||
recover_params = f"{darkside_obj['uid']}"
|
||||
for darkside_item in self.darkside_list:
|
||||
@@ -497,7 +513,8 @@ class HFMFDarkside(ReaderRequiredUnit):
|
||||
# auth key
|
||||
for key in key_list:
|
||||
key_bytes = bytearray.fromhex(key)
|
||||
auth_ret = self.cmd.auth_mf1_key(block_target, type_target, key_bytes)
|
||||
auth_ret = self.cmd.auth_mf1_key(
|
||||
block_target, type_target, key_bytes)
|
||||
if auth_ret.status == chameleon_status.Device.HF_TAG_OK:
|
||||
return key
|
||||
return None
|
||||
@@ -520,7 +537,8 @@ class BaseMF1AuthOpera(ReaderRequiredUnit):
|
||||
help="The block where the key of the card is known")
|
||||
parser.add_argument('-t', '--type', type=str, required=True, choices=type_choices,
|
||||
help="The key type of the tag")
|
||||
parser.add_argument('-k', '--key', type=str, required=True, metavar="hex", help="tag sector key")
|
||||
parser.add_argument('-k', '--key', type=str,
|
||||
required=True, metavar="hex", help="tag sector key")
|
||||
return parser
|
||||
|
||||
def get_param(self, args):
|
||||
@@ -562,9 +580,11 @@ class HFMFWRBL(BaseMF1AuthOpera):
|
||||
if not re.match(r"^[a-fA-F0-9]{32}$", args.data):
|
||||
raise ArgsParserError("Data must include 32 HEX symbols")
|
||||
param.data = bytearray.fromhex(args.data)
|
||||
resp = self.cmd.write_mf1_block(param.block, param.type, param.key, param.data)
|
||||
resp = self.cmd.write_mf1_block(
|
||||
param.block, param.type, param.key, param.data)
|
||||
if resp.status == chameleon_status.Device.HF_TAG_OK:
|
||||
print(f" - {colorama.Fore.GREEN}Write done.{colorama.Style.RESET_ALL}")
|
||||
print(
|
||||
f" - {colorama.Fore.GREEN}Write done.{colorama.Style.RESET_ALL}")
|
||||
else:
|
||||
print(f" - {colorama.Fore.RED}Write fail.{colorama.Style.RESET_ALL}")
|
||||
|
||||
@@ -573,7 +593,8 @@ class HFMFWRBL(BaseMF1AuthOpera):
|
||||
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")
|
||||
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
|
||||
@@ -628,7 +649,8 @@ class HFMFDetectionDecrypt(DeviceRequiredUnit):
|
||||
# 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)
|
||||
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])
|
||||
|
||||
@@ -638,19 +660,22 @@ class HFMFDetectionDecrypt(DeviceRequiredUnit):
|
||||
def on_exec(self, args: argparse.Namespace):
|
||||
buffer = bytearray()
|
||||
index = 0
|
||||
count = int.from_bytes(self.cmd.get_mf1_detection_count().data, "little", signed=False)
|
||||
count = int.from_bytes(
|
||||
self.cmd.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.get_mf1_detection_log(index).data
|
||||
recv_count = int(len(tmp) / HFMFDetectionDecrypt.detection_log_size)
|
||||
recv_count = int(
|
||||
len(tmp) / HFMFDetectionDecrypt.detection_log_size)
|
||||
index += recv_count
|
||||
buffer.extend(tmp)
|
||||
print(".", end="")
|
||||
print()
|
||||
print(f" - Download done ({len(buffer)}bytes), start parse and decrypt")
|
||||
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():
|
||||
@@ -662,18 +687,22 @@ class HFMFDetectionDecrypt(DeviceRequiredUnit):
|
||||
# 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)
|
||||
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)
|
||||
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']}")
|
||||
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']}")
|
||||
print(
|
||||
f" > Block {block}, B key result: {result_maps_for_uid[block]['B']}")
|
||||
return
|
||||
|
||||
|
||||
@@ -681,8 +710,10 @@ class HFMFDetectionDecrypt(DeviceRequiredUnit):
|
||||
class HFMFELoad(DeviceRequiredUnit):
|
||||
def args_parser(self) -> ArgumentParserNoExit or None:
|
||||
parser = ArgumentParserNoExit()
|
||||
parser.add_argument('-f', '--file', type=str, required=True, help="file path")
|
||||
parser.add_argument('-t', '--type', type=str, required=False, help="content type", choices=['bin', 'hex'])
|
||||
parser.add_argument('-f', '--file', type=str,
|
||||
required=True, help="file path")
|
||||
parser.add_argument('-t', '--type', type=str, required=False,
|
||||
help="content type", choices=['bin', 'hex'])
|
||||
return parser
|
||||
|
||||
# hf mf eload -f test.bin -t bin
|
||||
@@ -695,7 +726,8 @@ class HFMFELoad(DeviceRequiredUnit):
|
||||
elif file.endswith('.eml'):
|
||||
content_type = 'hex'
|
||||
else:
|
||||
raise Exception("Unknown file format, Specify content type with -t option")
|
||||
raise Exception(
|
||||
"Unknown file format, Specify content type with -t option")
|
||||
else:
|
||||
content_type = args.type
|
||||
buffer = bytearray()
|
||||
@@ -728,8 +760,10 @@ class HFMFELoad(DeviceRequiredUnit):
|
||||
class HFMFERead(DeviceRequiredUnit):
|
||||
def args_parser(self) -> ArgumentParserNoExit or None:
|
||||
parser = ArgumentParserNoExit()
|
||||
parser.add_argument('-f', '--file', type=str, required=True, help="file path")
|
||||
parser.add_argument('-t', '--type', type=str, required=False, help="content type", choices=['bin', 'hex'])
|
||||
parser.add_argument('-f', '--file', type=str,
|
||||
required=True, help="file path")
|
||||
parser.add_argument('-t', '--type', type=str, required=False,
|
||||
help="content type", choices=['bin', 'hex'])
|
||||
return parser
|
||||
|
||||
def on_exec(self, args: argparse.Namespace):
|
||||
@@ -740,7 +774,8 @@ class HFMFERead(DeviceRequiredUnit):
|
||||
elif file.endswith('.eml'):
|
||||
content_type = 'hex'
|
||||
else:
|
||||
raise Exception("Unknown file format, Specify content type with -t option")
|
||||
raise Exception(
|
||||
"Unknown file format, Specify content type with -t option")
|
||||
else:
|
||||
content_type = args.type
|
||||
|
||||
@@ -756,7 +791,8 @@ class HFMFERead(DeviceRequiredUnit):
|
||||
elif tag_type == chameleon_cmd.TagSpecificType.TAG_TYPE_MIFARE_4096:
|
||||
block_count = 256
|
||||
else:
|
||||
raise Exception("Card in current slot is not Mifare Classic/Plus in SL1 mode")
|
||||
raise Exception(
|
||||
"Card in current slot is not Mifare Classic/Plus in SL1 mode")
|
||||
|
||||
with open(file, 'wb') as fd:
|
||||
block = 0
|
||||
@@ -800,16 +836,20 @@ class HFMFSettings(DeviceRequiredUnit):
|
||||
def on_exec(self, args: argparse.Namespace):
|
||||
if args.gen1a != -1:
|
||||
self.cmd.set_mf1_gen1a_mode(args.gen1a)
|
||||
print(f' - Set gen1a mode to {"enabled" if args.gen1a else "disabled"} success')
|
||||
print(
|
||||
f' - Set gen1a mode to {"enabled" if args.gen1a else "disabled"} success')
|
||||
if args.gen2 != -1:
|
||||
self.cmd.set_mf1_gen2_mode(args.gen2)
|
||||
print(f' - Set gen2 mode to {"enabled" if args.gen2 else "disabled"} success')
|
||||
print(
|
||||
f' - Set gen2 mode to {"enabled" if args.gen2 else "disabled"} success')
|
||||
if args.coll != -1:
|
||||
self.cmd.set_mf1_block_anti_coll_mode(args.coll)
|
||||
print(f' - Set anti-collision mode to {"enabled" if args.coll else "disabled"} success')
|
||||
print(
|
||||
f' - Set anti-collision mode to {"enabled" if args.coll else "disabled"} success')
|
||||
if args.write != -1:
|
||||
self.cmd.set_mf1_write_mode(args.write)
|
||||
print(f' - Set write mode to {chameleon_cmd.MifareClassicWriteMode(args.write)} success')
|
||||
print(
|
||||
f' - Set write mode to {chameleon_cmd.MifareClassicWriteMode(args.write)} success')
|
||||
print(' - Emulator settings updated')
|
||||
|
||||
|
||||
@@ -817,9 +857,12 @@ class HFMFSettings(DeviceRequiredUnit):
|
||||
class HFMFSim(DeviceRequiredUnit):
|
||||
def args_parser(self) -> ArgumentParserNoExit or None:
|
||||
parser = ArgumentParserNoExit()
|
||||
parser.add_argument('--sak', type=str, required=True, help="Select AcKnowledge(hex)", metavar="hex")
|
||||
parser.add_argument('--atqa', type=str, required=True, help="Answer To Request(hex)", metavar="hex")
|
||||
parser.add_argument('--uid', type=str, required=True, help="Unique ID(hex)", metavar="hex")
|
||||
parser.add_argument('--sak', type=str, required=True,
|
||||
help="Select AcKnowledge(hex)", metavar="hex")
|
||||
parser.add_argument('--atqa', type=str, required=True,
|
||||
help="Answer To Request(hex)", metavar="hex")
|
||||
parser.add_argument('--uid', type=str, required=True,
|
||||
help="Unique ID(hex)", metavar="hex")
|
||||
return parser
|
||||
|
||||
# hf mf sim --sak 08 --atqa 0400 --uid DEADBEEF
|
||||
@@ -859,14 +902,16 @@ class LFEMRead(ReaderRequiredUnit):
|
||||
def on_exec(self, args: argparse.Namespace):
|
||||
resp = self.cmd.read_em_410x()
|
||||
id_hex = resp.data.hex()
|
||||
print(f" - EM410x ID(10H): {colorama.Fore.GREEN}{id_hex}{colorama.Style.RESET_ALL}")
|
||||
print(
|
||||
f" - EM410x ID(10H): {colorama.Fore.GREEN}{id_hex}{colorama.Style.RESET_ALL}")
|
||||
|
||||
|
||||
class LFEMCardRequiredUnit(DeviceRequiredUnit):
|
||||
|
||||
@staticmethod
|
||||
def add_card_arg(parser: ArgumentParserNoExit):
|
||||
parser.add_argument("--id", type=str, required=True, help="EM410x tag id", metavar="hex")
|
||||
parser.add_argument("--id", type=str, required=True,
|
||||
help="EM410x tag id", metavar="hex")
|
||||
return parser
|
||||
|
||||
def before_exec(self, args: argparse.Namespace):
|
||||
@@ -963,7 +1008,8 @@ class HWSlotList(DeviceRequiredUnit):
|
||||
# hw slot list
|
||||
def on_exec(self, args: argparse.Namespace):
|
||||
data = self.cmd.get_slot_info().data
|
||||
selected = chameleon_cmd.SlotNumber.from_fw(self.cmd.get_active_slot().data[0])
|
||||
selected = chameleon_cmd.SlotNumber.from_fw(
|
||||
self.cmd.get_active_slot().data[0])
|
||||
enabled = self.cmd.get_enabled_slots().data
|
||||
for slot in chameleon_cmd.SlotNumber:
|
||||
print(
|
||||
@@ -980,11 +1026,16 @@ class HWSlotList(DeviceRequiredUnit):
|
||||
if args.extend:
|
||||
config = self.cmd.get_mf1_emulator_settings().data
|
||||
print(' - Mifare Classic emulator settings:')
|
||||
print(f' Detection (mfkey32) mode: {"enabled" if config[0] else "disabled"}')
|
||||
print(f' Gen1A magic mode: {"enabled" if config[1] else "disabled"}')
|
||||
print(f' Gen2 magic mode: {"enabled" if config[2] else "disabled"}')
|
||||
print(f' Use anti-collision data from block 0: {"enabled" if config[3] else "disabled"}')
|
||||
print(f' Write mode: {chameleon_cmd.MifareClassicWriteMode(config[4])}')
|
||||
print(
|
||||
f' Detection (mfkey32) mode: {"enabled" if config[0] else "disabled"}')
|
||||
print(
|
||||
f' Gen1A magic mode: {"enabled" if config[1] else "disabled"}')
|
||||
print(
|
||||
f' Gen2 magic mode: {"enabled" if config[2] else "disabled"}')
|
||||
print(
|
||||
f' Use anti-collision data from block 0: {"enabled" if config[3] else "disabled"}')
|
||||
print(
|
||||
f' Write mode: {chameleon_cmd.MifareClassicWriteMode(config[4])}')
|
||||
|
||||
|
||||
@hw_slot.command('change', 'Set emulation tag slot activated.')
|
||||
@@ -1078,7 +1129,8 @@ class HWSlotEnableSet(SlotIndexRequireUnit):
|
||||
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])
|
||||
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
|
||||
@@ -1086,7 +1138,8 @@ class HWSlotEnableSet(SlotIndexRequireUnit):
|
||||
slot_num = args.slot
|
||||
enable = args.enable
|
||||
self.cmd.set_slot_enable(slot_num, enable)
|
||||
print(f' - Set slot {slot_num} {"enable" if enable else "disable"} success.')
|
||||
print(
|
||||
f' - Set slot {slot_num} {"enable" if enable else "disable"} success.')
|
||||
|
||||
|
||||
@lf_em_sim.command('set', 'Set simulated em410x card id')
|
||||
@@ -1123,7 +1176,8 @@ class HWSlotNickSet(SlotIndexRequireUnit, SenseTypeRequireUnit):
|
||||
parser = ArgumentParserNoExit()
|
||||
self.add_slot_args(parser)
|
||||
self.add_sense_type_args(parser)
|
||||
parser.add_argument('-n', '--name', type=str, required=True, help="Your tag nick name for slot")
|
||||
parser.add_argument('-n', '--name', type=str,
|
||||
required=True, help="Your tag nick name for slot")
|
||||
return parser
|
||||
|
||||
# hw slot nick set -s 1 -st 1 -n Save the test name
|
||||
@@ -1242,7 +1296,8 @@ class HWSettingsAnimationSet(DeviceRequiredUnit):
|
||||
def on_exec(self, args: argparse.Namespace):
|
||||
mode = args.mode
|
||||
self.cmd.set_settings_animation(mode)
|
||||
print("Animation mode change success. Do not forget to store your settings in flash!")
|
||||
print(
|
||||
"Animation mode change success. Do not forget to store your settings in flash!")
|
||||
|
||||
|
||||
@hw_settings.command('store', 'Store current settings to flash')
|
||||
@@ -1280,7 +1335,8 @@ class HWFactoryReset(DeviceRequiredUnit):
|
||||
parser.description = "Permanently wipes Chameleon to factory settings. " \
|
||||
"This will delete all your slot data and custom settings. " \
|
||||
"There's no going back."
|
||||
parser.add_argument("--i-know-what-im-doing", default=False, action="store_true", help="Just to be sure :)")
|
||||
parser.add_argument("--i-know-what-im-doing", default=False,
|
||||
action="store_true", help="Just to be sure :)")
|
||||
return parser
|
||||
|
||||
def on_exec(self, args: argparse.Namespace):
|
||||
@@ -1312,7 +1368,8 @@ class HWBatteryInfo(DeviceRequiredUnit):
|
||||
print(f" voltage -> {voltage}mV")
|
||||
print(f" percentage -> {percentage}%")
|
||||
if percentage < HWBatteryInfo.BATTERY_LOW_LEVEL:
|
||||
print(f"{colorama.Fore.RED}[!] Low battery, please charge.{colorama.Style.RESET_ALL}")
|
||||
print(
|
||||
f"{colorama.Fore.RED}[!] Low battery, please charge.{colorama.Style.RESET_ALL}")
|
||||
|
||||
|
||||
@hw_settings_button_press.command('get', 'Get button press function of Button A and Button B.')
|
||||
@@ -1331,8 +1388,10 @@ class HWButtonSettingsGet(DeviceRequiredUnit):
|
||||
for button in button_list:
|
||||
resp = self.cmd.get_button_press_fun(button)
|
||||
resp_long = self.cmd.get_long_button_press_fun(button)
|
||||
button_fn = chameleon_cmd.ButtonPressFunction.from_int(resp.data[0])
|
||||
button_long_fn = chameleon_cmd.ButtonPressFunction.from_int(resp_long.data[0])
|
||||
button_fn = chameleon_cmd.ButtonPressFunction.from_int(
|
||||
resp.data[0])
|
||||
button_long_fn = chameleon_cmd.ButtonPressFunction.from_int(
|
||||
resp_long.data[0])
|
||||
print(f" - {colorama.Fore.GREEN}{button} {colorama.Fore.YELLOW}short{colorama.Style.RESET_ALL}:"
|
||||
" {button_fn}")
|
||||
print(f" usage: {button_fn.usage()}")
|
||||
|
||||
@@ -70,7 +70,8 @@ class ChameleonCom:
|
||||
error = None
|
||||
try:
|
||||
# open serial port
|
||||
self.serial_instance = serial.Serial(port=port, baudrate=115200)
|
||||
self.serial_instance = serial.Serial(
|
||||
port=port, baudrate=115200)
|
||||
except Exception as e:
|
||||
error = e
|
||||
finally:
|
||||
@@ -98,7 +99,8 @@ class ChameleonCom:
|
||||
:return:
|
||||
"""
|
||||
if not self.isOpen():
|
||||
raise NotOpenException("Please call open() function to start device.")
|
||||
raise NotOpenException(
|
||||
"Please call open() function to start device.")
|
||||
|
||||
@staticmethod
|
||||
def lrc_calc(array):
|
||||
@@ -196,7 +198,8 @@ class ChameleonCom:
|
||||
if callable(fn_call):
|
||||
# delete wait task from map
|
||||
del self.wait_response_map[data_cmd]
|
||||
fn_call(data_cmd, data_status, data_response)
|
||||
fn_call(data_cmd, data_status,
|
||||
data_response)
|
||||
else:
|
||||
self.wait_response_map[data_cmd]['response'] = Response(data_cmd, data_status,
|
||||
data_response)
|
||||
@@ -227,7 +230,8 @@ 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}
|
||||
# set start time
|
||||
@@ -258,7 +262,8 @@ class ChameleonCom:
|
||||
if time.time() > self.wait_response_map[task_cmd]['end_time']:
|
||||
if 'callback' in self.wait_response_map[task_cmd]:
|
||||
# not sync, call function to notify timeout.
|
||||
self.wait_response_map[task_cmd]['callback'](task_cmd, None, None)
|
||||
self.wait_response_map[task_cmd]['callback'](
|
||||
task_cmd, None, None)
|
||||
else:
|
||||
# sync mode, set timeout flag
|
||||
self.wait_response_map[task_cmd]['is_timeout'] = True
|
||||
@@ -303,7 +308,8 @@ 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)
|
||||
|
||||
@@ -43,7 +43,8 @@ def parse_nested_nt_acquire_group(data: bytearray):
|
||||
"""
|
||||
group = []
|
||||
if len(data) % 9 != 0:
|
||||
raise ValueError("Nt data length error, except: { nt(4byte), nt_enc(4byte), par(1byte) } * N")
|
||||
raise ValueError(
|
||||
"Nt data length error, except: { nt(4byte), nt_enc(4byte), par(1byte) } * N")
|
||||
i = 0
|
||||
while i < len(data):
|
||||
group.append({
|
||||
|
||||
@@ -57,9 +57,11 @@ def expect_response(accepted_responses: Union[int, list[int]]):
|
||||
|
||||
if ret.status not in accepted_responses:
|
||||
if ret.status in chameleon_status.Device and ret.status in chameleon_status.message:
|
||||
raise UnexpectedResponseError(chameleon_status.message[ret.status])
|
||||
raise UnexpectedResponseError(
|
||||
chameleon_status.message[ret.status])
|
||||
else:
|
||||
raise UnexpectedResponseError(f"Unexpected response and unknown status {ret.status}")
|
||||
raise UnexpectedResponseError(
|
||||
f"Unexpected response and unknown status {ret.status}")
|
||||
|
||||
return ret
|
||||
|
||||
@@ -137,7 +139,8 @@ class CustomNestedCompleter(NestedCompleter):
|
||||
elif isinstance(value, dict):
|
||||
options[key] = cls.from_nested_dict(value)
|
||||
elif isinstance(value, set):
|
||||
options[key] = cls.from_nested_dict({item: None for item in value})
|
||||
options[key] = cls.from_nested_dict(
|
||||
{item: None for item in value})
|
||||
elif isinstance(value, CLITree):
|
||||
if value.cls:
|
||||
# CLITree is a standalone command
|
||||
@@ -160,7 +163,8 @@ class CustomNestedCompleter(NestedCompleter):
|
||||
for child_node in node.children:
|
||||
if child_node.cls and child_node.cls().args_parser():
|
||||
# CLITree is a standalone command with arguments
|
||||
options[child_node.name] = ArgparseCompleter(child_node.cls().args_parser())
|
||||
options[child_node.name] = ArgparseCompleter(
|
||||
child_node.cls().args_parser())
|
||||
else:
|
||||
# CLITree is a command group
|
||||
options[child_node.name] = cls.from_clitree(child_node)
|
||||
@@ -240,7 +244,8 @@ class ArgparseCompleter(Completer):
|
||||
parsed.append(value)
|
||||
|
||||
if check_arg(unparsed):
|
||||
parsed, unparsed, suggestions = self.check_tokens(parsed, unparsed)
|
||||
parsed, unparsed, suggestions = self.check_tokens(
|
||||
parsed, unparsed)
|
||||
|
||||
else:
|
||||
# Show all possible values
|
||||
@@ -251,7 +256,8 @@ class ArgparseCompleter(Completer):
|
||||
else:
|
||||
# No choices, process further arguments
|
||||
if check_arg(unparsed):
|
||||
parsed, unparsed, suggestions = self.check_tokens(parsed, unparsed)
|
||||
parsed, unparsed, suggestions = self.check_tokens(
|
||||
parsed, unparsed)
|
||||
break
|
||||
elif any(opt.startswith(token) for opt in action.option_strings):
|
||||
for opt in action.option_strings:
|
||||
|
||||
Reference in New Issue
Block a user