Files
ChameleonUltra/software/script/chameleon_cli_main.py
2023-04-08 12:34:20 +02:00

220 lines
10 KiB
Python

import argparse
import os
import platform
import sys
import traceback
import types
import chameleon_com
import chameleon_cmd
import colorama
import chameleon_cli_unit
ULTRA = r"""
╦ ╦╦ ╔╦╗╦═╗╔═╗
███████ ║ ║║ ║ ╠╦╝╠═╣
╚═╝╩═╝╩ ╩╚═╩ ╩
"""
LITE = r"""
╦ ╦╔╦╗╔═╗
███████ ║ ║ ║ ║╣
╩═╝╩ ╩ ╚═╝
"""
# create by http://patorjk.com/software/taag/#p=display&f=ANSI%20Shadow&t=Chameleon%20Ultra
BANNER = f"""
██████╗██╗ ██╗ █████╗ ███╗ ███╗███████╗██╗ ███████╗ ██████╗ ███╗ ██╗
██╔════╝██║ ██║██╔══██╗████╗ ████║██╔════╝██║ ██╔════╝██╔═══██╗████╗ ██║
██║ ███████║███████║██╔████╔██║█████╗ ██║ █████╗ ██║ ██║██╔██╗ ██║
██║ ██╔══██║██╔══██║██║╚██╔╝██║██╔══╝ ██║ ██╔══╝ ██║ ██║██║╚██╗██║
╚██████╗██║ ██║██║ ██║██║ ╚═╝ ██║███████╗███████╗███████╗╚██████╔╝██║ ╚████║
╚═════╝╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚══════╝╚══════╝ ╚═════╝ ╚═╝ ╚═══╝
"""
def new_uint(unit_clz, help_msg):
"""
new a uint dict object
:param unit_clz: unit implement class
:param help_msg: unit usage
:return: a dict...
"""
return {
'unit': unit_clz,
'help': help_msg,
}
class ChameleonCLI:
"""
CLI for chameleon
"""
def __init__(self):
self.cmd_maps = {
'hw': {
'connect': new_uint(chameleon_cli_unit.HWConnect, "Connect to chameleon by serial port"),
'mode': {
'set': new_uint(chameleon_cli_unit.HWModeSet, "Change device mode to tag reader or tag emulator"),
'get': new_uint(chameleon_cli_unit.HWModeGet, "Get current device mode"),
'help': "Device mode get/set"
},
'slot': {
'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"),
'nick': {
'set': new_uint(chameleon_cli_unit.HWSlotNickSet, "Set tag nick name for slot"),
'get': new_uint(chameleon_cli_unit.HWSlotNickGet, "Get tag nick name for slot"),
'help': "Get/Set tag nick name for slot",
},
'update': new_uint(chameleon_cli_unit.HWSlotUpdate, "Update config & data to device flash"),
'openall': new_uint(chameleon_cli_unit.HWSlotOpenAll, "Open all slot and set to default data"),
'help': "Emulation tag slot.",
},
'dfu': new_uint(chameleon_cli_unit.HWDFU, "Restart application to bootloader mode(Not yet implement dfu)."),
'help': "hardware controller",
},
'hf': {
'14a': {
'scan': new_uint(chameleon_cli_unit.HF14AScan, "Scan 14a tag, and print basic information"),
'info': new_uint(chameleon_cli_unit.HF14AInfo, "Scan 14a tag, and print detail information"),
'help': "ISO14443-a tag read/write/info...",
},
'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"),
'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"),
'eload': new_uint(chameleon_cli_unit.HFMFELoad, "Load data to emulator memory"),
'help': "Mifare Classic mini/1/2/4, attack/read/write"
},
'help': "high frequency tag/reader",
},
'lf': {
'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"),
'sim': new_uint(chameleon_cli_unit.LFEMSim, "Simulation a em410x id card"),
'help': "EM410x read/write/emulator",
},
'help': "low frequency tag/reader",
}
}
# new a device communication instance(only communication)
self.device_com = chameleon_com.ChameleonCom()
@staticmethod
def print_banner():
"""
print chameleon ascii banner
:return:
"""
print(colorama.Fore.YELLOW + BANNER)
def parse_cli_cmd(self, cmd_str):
"""
parse cmd from str
:param cmd_str:
:return:
"""
cmds = cmd_str.split(" ")
cmd_maps: dict or types.FunctionType = self.cmd_maps
cmd_end = ""
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
else: # CMD not found
break
cmd_end_position = cmd_str.index(cmd_end) + len(cmd_end) + 1
return cmd_maps, (cmd_str[:cmd_end_position], cmd_str[cmd_end_position:])
def startCLI(self):
"""
start listen input.
:return:
"""
self.print_banner()
while True:
# wait user input
status = f"{colorama.Fore.GREEN}USB" if self.device_com.isOpen() else f"{colorama.Fore.RED}Offline"
print(f"[{status}{colorama.Style.RESET_ALL}] chameleon --> ", end="")
cmd_str = input().strip()
# clear screen
if cmd_str == "clear":
if platform.system() == 'Windows':
os.system("cls")
elif platform.system() == 'Linux':
os.system("clear")
else:
print("No screen clear implement")
continue
if cmd_str == "exit":
print("Bye, thank you. ^.^ ")
sys.exit(996)
# parse cmd
cmd_map, args_str = self.parse_cli_cmd(cmd_str)
is_exec_map = 'unit' in cmd_map
if is_exec_map:
# new a unit instance
unit_clz = cmd_map['unit']
if callable(unit_clz):
unit: chameleon_cli_unit.BaseCLIUnit = unit_clz()
else:
raise TypeError("CMD unit is not a 'BaseCLIUnit'")
# set variables of required
unit.device_com = self.device_com
# parse args
args_parse_result = unit.args_parser()
if args_parse_result is not None:
args: argparse.ArgumentParser = args_parse_result
args.prog = args_str[0]
try:
args_parse_result = args.parse_args(args_str[1].split())
except chameleon_cli_unit.ArgsParserError as e:
args.print_usage()
print(str(e).strip(), end="\n\n")
continue
except chameleon_cli_unit.ParserExitIntercept:
# don't exit process.
continue
# noinspection PyBroadException
try:
# before process cmd, we need to do something...
if not unit.before_exec(args_parse_result):
continue
# start process cmd
unit.on_exec(args_parse_result)
except (chameleon_cmd.NegativeResponseError, chameleon_cli_unit.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}")
elif isinstance(cmd_map, dict):
print("".ljust(18, "-") + "".ljust(10) + "".ljust(30, "-"))
for map_key in cmd_map:
map_item = cmd_map[map_key]
if 'help' in map_item:
cmd_title = f"{colorama.Fore.GREEN}{map_key}{colorama.Style.RESET_ALL}"
help_line = (f" - {cmd_title}".ljust(37)) + f"[ {map_item['help']} ]"
print(help_line)
if __name__ == '__main__':
colorama.init(autoreset=True)
ChameleonCLI().startCLI()