diff --git a/firmware/application/src/app_cmd.c b/firmware/application/src/app_cmd.c index 2bbbdbd..ac22dd8 100644 --- a/firmware/application/src/app_cmd.c +++ b/firmware/application/src/app_cmd.c @@ -463,7 +463,7 @@ data_frame_tx_t* cmd_processor_set_mf1_anti_collision_res(uint16_t cmd, uint16_t } else { uint8_t uid_length = length - 3; if (is_valid_uid_size(uid_length)) { - nfc_tag_14a_coll_res_referen_t* info = get_miafre_coll_res(); + nfc_tag_14a_coll_res_referen_t* info = get_mifare_coll_res(); // copy sak info->sak[0] = data[0]; // copy atqa @@ -528,6 +528,124 @@ data_frame_tx_t* cmd_processor_get_slot_tag_nick_name(uint16_t cmd, uint16_t sta return data_frame_make(cmd, status, length, data); } +data_frame_tx_t* cmd_processor_get_mf1_info(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) { + uint8_t mf1_info[5] = {}; + mf1_info[0] = nfc_tag_mf1_is_detection_enable(); + mf1_info[1] = nfc_tag_mf1_is_gen1a_magic_mode(); + mf1_info[2] = nfc_tag_mf1_is_gen2_magic_mode(); + mf1_info[3] = nfc_tag_mf1_is_use_mf1_coll_res(); + nfc_tag_mf1_write_mode_t write_mode = nfc_tag_mf1_get_write_mode(); + if (write_mode == NFC_TAG_MF1_WRITE_NORMAL) { + mf1_info[4] = 0; + } else if (write_mode == NFC_TAG_MF1_WRITE_DENIED) { + mf1_info[4] = 1; + } else if (write_mode == NFC_TAG_MF1_WRITE_DECEIVE) { + mf1_info[4] = 2; + } else if (write_mode == NFC_TAG_MF1_WRITE_SHADOW) { + mf1_info[4] = 3; + } + return data_frame_make(cmd, STATUS_DEVICE_SUCCESS, 5, mf1_info); +} + +data_frame_tx_t* cmd_processor_get_mf1_gen1a_magic_mode(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) { + if (nfc_tag_mf1_is_gen1a_magic_mode()) { + status = 1; + } else { + status = 0; + } + return data_frame_make(cmd, STATUS_DEVICE_SUCCESS, 1, (uint8_t*)&status); +} + +data_frame_tx_t* cmd_processor_set_mf1_gen1a_magic_mode(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) { + if (length == 1 && (data[0] == 0 || data[0] == 1)) { + nfc_tag_mf1_set_gen1a_magic_mode(data[0]); + status = STATUS_DEVICE_SUCCESS; + } else { + status = STATUS_PAR_ERR; + } + return data_frame_make(cmd, status, 0, NULL); +} + +data_frame_tx_t* cmd_processor_get_mf1_gen2_magic_mode(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) { + if (nfc_tag_mf1_is_gen2_magic_mode()) { + status = 1; + } else { + status = 0; + } + return data_frame_make(cmd, STATUS_DEVICE_SUCCESS, 1, (uint8_t*)&status); +} + +data_frame_tx_t* cmd_processor_set_mf1_gen2_magic_mode(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) { + if (length == 1 && (data[0] == 0 || data[0] == 1)) { + nfc_tag_mf1_set_gen2_magic_mode(data[0]); + status = STATUS_DEVICE_SUCCESS; + } else { + status = STATUS_PAR_ERR; + } + return data_frame_make(cmd, status, 0, NULL); +} + +data_frame_tx_t* cmd_processor_get_mf1_use_coll_res(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) { + if (nfc_tag_mf1_is_use_mf1_coll_res()) { + status = 1; + } else { + status = 0; + } + return data_frame_make(cmd, STATUS_DEVICE_SUCCESS, 1, (uint8_t*)&status); +} + +data_frame_tx_t* cmd_processor_set_mf1_use_coll_res(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) { + if (length == 1 && (data[0] == 0 || data[0] == 1)) { + nfc_tag_mf1_set_use_mf1_coll_res(data[0]); + status = STATUS_DEVICE_SUCCESS; + } else { + status = STATUS_PAR_ERR; + } + return data_frame_make(cmd, status, 0, NULL); +} + +data_frame_tx_t* cmd_processor_get_mf1_write_mode(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) { + nfc_tag_mf1_write_mode_t write_mode = nfc_tag_mf1_get_write_mode(); + if (write_mode == NFC_TAG_MF1_WRITE_NORMAL) { + status = 0; + } else if (write_mode == NFC_TAG_MF1_WRITE_DENIED) { + status = 1; + } else if (write_mode == NFC_TAG_MF1_WRITE_DECEIVE) { + status = 2; + } else if (write_mode == NFC_TAG_MF1_WRITE_SHADOW) { + status = 3; + } + return data_frame_make(cmd, STATUS_DEVICE_SUCCESS, 1, (uint8_t*)&status); +} + +data_frame_tx_t* cmd_processor_set_mf1_write_mode(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) { + if (length == 1 && (data[0] >= 0 || data[0] <= 3)) { + uint8_t mode = data[0]; + if (mode == 0) { + nfc_tag_mf1_set_write_mode(NFC_TAG_MF1_WRITE_NORMAL); + } else if (mode == 1) { + nfc_tag_mf1_set_write_mode(NFC_TAG_MF1_WRITE_DENIED); + } else if (mode == 2) { + nfc_tag_mf1_set_write_mode(NFC_TAG_MF1_WRITE_DECEIVE); + } else if (mode == 3) { + nfc_tag_mf1_set_write_mode(NFC_TAG_MF1_WRITE_SHADOW); + } + status = STATUS_DEVICE_SUCCESS; + } else { + status = STATUS_PAR_ERR; + } + return data_frame_make(cmd, status, 0, NULL); +} + +data_frame_tx_t* cmd_processor_get_enabled_slots(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) { + uint8_t slot_info[8] = {}; + for (uint8_t slot = 0; slot < 8; slot++) { + slot_info[slot] = tag_emulation_slot_is_enable(slot); + } + + return data_frame_make(cmd, STATUS_DEVICE_SUCCESS, 8, slot_info); +} + #if defined(PROJECT_CHAMELEON_ULTRA) @@ -614,6 +732,8 @@ static cmd_data_map_t m_data_cmd_map[] = { { DATA_CMD_GET_ACTIVE_SLOT, NULL, cmd_processor_get_activated_slot, NULL }, { DATA_CMD_GET_SLOT_INFO, NULL, cmd_processor_get_slot_info, NULL }, { DATA_CMD_WIPE_FDS, NULL, cmd_processor_wipe_fds, NULL }, + { DATA_CMD_GET_ENABLED_SLOTS, NULL, cmd_processor_get_enabled_slots, NULL }, + { DATA_CMD_SET_EM410X_EMU_ID, NULL, cmd_processor_set_em410x_emu_id, NULL }, @@ -624,6 +744,15 @@ static cmd_data_map_t m_data_cmd_map[] = { { DATA_CMD_GET_MF1_DETECTION_RESULT, NULL, cmd_processor_get_mf1_detection_log, NULL }, { DATA_CMD_LOAD_MF1_BLOCK_DATA, NULL, cmd_processor_set_mf1_emulator_block, NULL }, { DATA_CMD_SET_MF1_ANTI_COLLISION_RES, NULL, cmd_processor_set_mf1_anti_collision_res, NULL }, + { DATA_CMD_GET_MF1_EMULATOR_CONFIG, NULL, cmd_processor_get_mf1_info, NULL }, + { DATA_CMD_GET_MF1_GEN1A_MODE, NULL, cmd_processor_get_mf1_gen1a_magic_mode, NULL }, + { DATA_CMD_SET_MF1_GEN1A_MODE, NULL, cmd_processor_set_mf1_gen1a_magic_mode, NULL }, + { DATA_CMD_GET_MF1_GEN2_MODE, NULL, cmd_processor_get_mf1_gen2_magic_mode, NULL }, + { DATA_CMD_SET_MF1_GEN2_MODE, NULL, cmd_processor_set_mf1_gen2_magic_mode, NULL }, + { DATA_CMD_GET_MF1_USE_FIRST_BLOCK_COLL, NULL, cmd_processor_get_mf1_use_coll_res, NULL }, + { DATA_CMD_SET_MF1_USE_FIRST_BLOCK_COLL, NULL, cmd_processor_set_mf1_use_coll_res, NULL }, + { DATA_CMD_GET_MF1_WRITE_MODE, NULL, cmd_processor_get_mf1_write_mode, NULL }, + { DATA_CMD_SET_MF1_WRITE_MODE, NULL, cmd_processor_set_mf1_write_mode, NULL }, { DATA_CMD_SET_SLOT_TAG_NICK, NULL, cmd_processor_set_slot_tag_nick_name, NULL }, { DATA_CMD_GET_SLOT_TAG_NICK, NULL, cmd_processor_get_slot_tag_nick_name, NULL }, diff --git a/firmware/application/src/data_cmd.h b/firmware/application/src/data_cmd.h index 2050fa8..ce00e3a 100644 --- a/firmware/application/src/data_cmd.h +++ b/firmware/application/src/data_cmd.h @@ -28,6 +28,7 @@ #define DATA_CMD_GET_ACTIVE_SLOT (1018) #define DATA_CMD_GET_SLOT_INFO (1019) #define DATA_CMD_WIPE_FDS (1020) +#define DATA_CMD_GET_ENABLED_SLOTS (1023) // // ****************************************************************** @@ -69,6 +70,17 @@ // #define DATA_CMD_LOAD_MF1_BLOCK_DATA (4000) #define DATA_CMD_SET_MF1_ANTI_COLLISION_RES (4001) + +#define DATA_CMD_GET_MF1_EMULATOR_CONFIG (4009) +#define DATA_CMD_GET_MF1_GEN1A_MODE (4010) +#define DATA_CMD_SET_MF1_GEN1A_MODE (4011) +#define DATA_CMD_GET_MF1_GEN2_MODE (4012) +#define DATA_CMD_SET_MF1_GEN2_MODE (4013) +#define DATA_CMD_GET_MF1_USE_FIRST_BLOCK_COLL (4014) +#define DATA_CMD_SET_MF1_USE_FIRST_BLOCK_COLL (4015) +#define DATA_CMD_GET_MF1_WRITE_MODE (4016) +#define DATA_CMD_SET_MF1_WRITE_MODE (4017) + // // ****************************************************************** diff --git a/firmware/application/src/rfid/nfctag/hf/nfc_mf1.c b/firmware/application/src/rfid/nfctag/hf/nfc_mf1.c index 6402327..920511f 100644 --- a/firmware/application/src/rfid/nfctag/hf/nfc_mf1.c +++ b/firmware/application/src/rfid/nfctag/hf/nfc_mf1.c @@ -38,14 +38,14 @@ NRF_LOG_MODULE_REGISTER(); #define CMD_AUTH_A 0x60 #define CMD_AUTH_B 0x61 #define CMD_AUTH_FRAME_SIZE 2 /* Bytes without CRCA */ -#define CMD_AUTH_RB_FRAME_SIZE 4 /* Bytes */ -#define CMD_AUTH_AB_FRAME_SIZE 8 /* Bytes */ -#define CMD_AUTH_BA_FRAME_SIZE 4 /* Bytes */ +#define CMD_AUTH_RB_FRAME_SIZE 4 /* Bytes */ +#define CMD_AUTH_AB_FRAME_SIZE 8 /* Bytes */ +#define CMD_AUTH_BA_FRAME_SIZE 4 /* Bytes */ #define CMD_HALT 0x50 -#define CMD_HALT_FRAME_SIZE 2 /* Bytes without CRCA */ +#define CMD_HALT_FRAME_SIZE 2 /* Bytes without CRCA */ #define CMD_READ 0x30 #define CMD_READ_FRAME_SIZE 2 /* Bytes without CRCA */ -#define CMD_READ_RESPONSE_FRAME_SIZE 16 /* Bytes without CRCA */ +#define CMD_READ_RESPONSE_FRAME_SIZE 16 /* Bytes without CRCA */ #define CMD_WRITE 0xA0 #define CMD_WRITE_FRAME_SIZE 2 /* Bytes without CRCA */ #define CMD_DECREMENT 0xC0 @@ -54,8 +54,6 @@ NRF_LOG_MODULE_REGISTER(); #define CMD_INCREMENT_FRAME_SIZE 2 /* Bytes without CRCA */ #define CMD_RESTORE 0xC2 #define CMD_RESTORE_FRAME_SIZE 2 /* Bytes without CRCA */ -#define CMD_SIG_READ 0xC2 -#define CMD_SIG_READ_FRAME_SIZE 1 /* Bytes without CRCA */ #define CMD_TRANSFER 0xB0 #define CMD_TRANSFER_FRAME_SIZE 2 /* Bytes without CRCA */ @@ -311,9 +309,18 @@ void ValueToBlock(uint8_t *Block, uint32_t Value) { /** @brief mf1获取一个随机数 * @param nonce 随机数的Buffer */ -void nfc_tag_mf1_random_nonce(uint8_t nonce[4]) { +void nfc_tag_mf1_random_nonce(uint8_t nonce[4], bool isNested) { // 使用rand进行快速产生随机数,性能损耗较小 - num_to_bytes(rand(), 4, nonce); + // isNested provides more randomness for hardnested attack + if (isNested) { + nonce[0] = rand() & 0xff; + nonce[1] = rand() & 0xff; + nonce[2] = rand() & 0xff; + nonce[3] = rand() & 0xff; + } else { + // fast for most readers + num_to_bytes(rand(), 4, nonce); + } } /** @@ -494,7 +501,7 @@ void nfc_tag_mf1_state_handler(uint8_t* p_data, uint16_t szDataBits) { m_tag_trailer_info = (nfc_tag_mf1_trailer_info_t*)m_tag_information->memory[BlockEnd]; // 生成随机数 - nfc_tag_mf1_random_nonce(CardNonce); + nfc_tag_mf1_random_nonce(CardNonce, false); // 根据卡随机数预先计算读卡器应答 for (uint8_t i = 0; i < sizeof(ReaderResponse); i++) { @@ -674,10 +681,10 @@ void nfc_tag_mf1_state_handler(uint8_t* p_data, uint16_t szDataBits) { case CMD_READ: { // 保存当前操作的块地址 CurrentAddress = p_data[1]; + // 生成访问控制,用于下面的数据访问控制 + uint8_t Acc = abTrailorAccessConditions[ GetAccessCondition(CurrentAddress) ][ KeyInUse ]; // 读取命令。从内存中读取数据并附加CRCA。注意:读取操作受到控制位的限制,但是目前我们只限制控制位的读取 - if ((p_data[1] < 128 && (p_data[1] & 3) == 3) || ((p_data[1] & 15) == 15)) { - // 生成访问控制,用于下面的数据访问控制 - uint8_t Acc = abTrailorAccessConditions[ GetAccessCondition(CurrentAddress) ][ KeyInUse ]; + if ((CurrentAddress < 128 && (CurrentAddress & 3) == 3) || ((CurrentAddress & 15) == 15)) { // 清空一下buffer,避免缓存的数据影响到后续操作 memset(m_tag_tx_buffer.tx_raw_buffer, 0x00, sizeof(m_tag_tx_buffer.tx_raw_buffer)); // 让这块数据区域变成我们需要的尾部块类型 @@ -717,7 +724,7 @@ void nfc_tag_mf1_state_handler(uint8_t* p_data, uint16_t szDataBits) { } case CMD_WRITE: { // 正常的卡不允许写block0,不然会被CUID防火墙识别到 - if (p_data[1] == 0x00) { + if (p_data[1] == 0x00 && !m_tag_information->config.mode_gen2_magic) { // 直接重置14a的状态机,让标签休眠 nfc_tag_14a_set_state(NFC_TAG_STATE_14A_HALTED); // 告知一下读头此操作不被允许 @@ -823,7 +830,7 @@ void nfc_tag_mf1_state_handler(uint8_t* p_data, uint16_t szDataBits) { m_tag_trailer_info = (nfc_tag_mf1_trailer_info_t*)m_tag_information->memory[BlockEnd]; // 生成随机数 - nfc_tag_mf1_random_nonce(CardNonce); + nfc_tag_mf1_random_nonce(CardNonce, true); // 根据卡随机数预先计算读卡器响应 for (uint8_t i = 0; i < sizeof(ReaderResponse); i++) { @@ -1041,7 +1048,7 @@ void nfc_tag_mf1_state_handler(uint8_t* p_data, uint16_t szDataBits) { /** * @brief 提供mifare标签必要的防冲突资源(仅提供指针) */ -nfc_tag_14a_coll_res_referen_t* get_miafre_coll_res() { +nfc_tag_14a_coll_res_referen_t* get_mifare_coll_res() { // 根据当前的互通配置,选择性的返回其中配置的数据,假设开启了数据互通,那么我们还需要确保当前模拟的卡是4BYTE的 if (m_tag_information->config.use_mf1_coll_res && m_tag_information->res_coll.size == NFC_TAG_14A_UID_SINGLE_SIZE) { // 获得数据区域的厂商信息 @@ -1119,7 +1126,7 @@ int nfc_tag_mf1_data_loadcb(tag_specific_type_t type, tag_data_buffer_t* buffer) m_tag_type = type; // 注册14a通信管理接口 nfc_tag_14a_handler_t handler_for_14a = { - .get_coll_res = get_miafre_coll_res, + .get_coll_res = get_mifare_coll_res, .cb_state = nfc_tag_mf1_state_handler, .cb_reset = nfc_tag_mf1_reset_handler, }; @@ -1166,6 +1173,7 @@ bool nfc_tag_mf1_data_factory(uint8_t slot, tag_specific_type_t tag_type) { // default mf1 config p_mf1_information->config.mode_gen1a_magic = false; + p_mf1_information->config.mode_gen2_magic = false; p_mf1_information->config.use_mf1_coll_res = false; p_mf1_information->config.mode_block_write = NFC_TAG_MF1_WRITE_NORMAL; p_mf1_information->config.detection_enable = false; @@ -1204,3 +1212,44 @@ void nfc_tag_mf1_detection_log_clear(void) { uint32_t nfc_tag_mf1_detection_log_count(void) { return m_auth_log.count; } + +// Set gen1a magic mode +void nfc_tag_mf1_set_gen1a_magic_mode(bool enable) { + m_tag_information->config.mode_gen1a_magic = enable; +} + +// Is in gen1a magic mode? +bool nfc_tag_mf1_is_gen1a_magic_mode(void) { + return m_tag_information->config.mode_gen1a_magic; +} + +// Set gen2 magic mode +void nfc_tag_mf1_set_gen2_magic_mode(bool enable) { + m_tag_information->config.mode_gen2_magic = enable; +} + +// Is in gen2 magic mode? +bool nfc_tag_mf1_is_gen2_magic_mode(void) { + return m_tag_information->config.mode_gen2_magic; +} + +// Set anti collision data from block 0 +void nfc_tag_mf1_set_use_mf1_coll_res(bool enable) { + m_tag_information->config.use_mf1_coll_res = enable; +} + +// Get is anti collision data from block 0 +bool nfc_tag_mf1_is_use_mf1_coll_res(void) { + return m_tag_information->config.use_mf1_coll_res; +} + +// Set write mode +void nfc_tag_mf1_set_write_mode(nfc_tag_mf1_write_mode_t write_mode) { + m_tag_information->config.mode_block_write = write_mode; +} + +// Get write mode +nfc_tag_mf1_write_mode_t nfc_tag_mf1_get_write_mode(void) { + return m_tag_information->config.mode_block_write; +} + diff --git a/firmware/application/src/rfid/nfctag/hf/nfc_mf1.h b/firmware/application/src/rfid/nfctag/hf/nfc_mf1.h index 2a8a228..ea35b25 100644 --- a/firmware/application/src/rfid/nfctag/hf/nfc_mf1.h +++ b/firmware/application/src/rfid/nfctag/hf/nfc_mf1.h @@ -44,10 +44,10 @@ typedef enum { // mf1配置 typedef struct { /** - * 正常写入模式(根据当前的状态去正常写入,受控制位和后门卡影响) - * 拒绝写入模式(类似控制位锁死,直接拒绝任何写入,返回nack) - * 欺诈写入模式(表面上返回ack表示写入成功,其实连RAM都不写入) - * 影子写入模式(写入到RAM里面,并且返回ack表示成功,但是不保存到flash里面) + * Normal write mode (write normally according to the current state, affected by the control bit and the back door card) + * Deny write mode (similar to control bit lock, directly reject any write, return nack) + * Fraudulent writing mode (on the surface, returning ack indicates that the writing is successful, but in fact, even RAM is not written) + * Shadow write mode (write to RAM, and return ack to indicate success, but not save to flash) * @see nfc_tag_mf1_write_mode_t */ nfc_tag_mf1_write_mode_t mode_block_write; @@ -66,8 +66,10 @@ typedef struct { * 使能侦测,将自动记录mf1的验证日志 */ uint8_t detection_enable: 1; + // Allow to write block 0 (CUID/gen2 mode) + uint8_t mode_gen2_magic: 1; // 保留 - uint8_t reserved1: 5; + uint8_t reserved1: 4; uint8_t reserved2; uint8_t reserved3; } nfc_tag_mf1_configure_t; @@ -140,6 +142,15 @@ void nfc_tag_mf1_set_detection_enable(bool enable); bool nfc_tag_mf1_is_detection_enable(void); void nfc_tag_mf1_detection_log_clear(void); uint32_t nfc_tag_mf1_detection_log_count(void); -nfc_tag_14a_coll_res_referen_t* get_miafre_coll_res(void); +nfc_tag_14a_coll_res_referen_t* get_mifare_coll_res(void); +void nfc_tag_mf1_set_gen1a_magic_mode(bool enable); +bool nfc_tag_mf1_is_gen1a_magic_mode(void); +void nfc_tag_mf1_set_gen2_magic_mode(bool enable); +bool nfc_tag_mf1_is_gen2_magic_mode(void); +void nfc_tag_mf1_set_use_mf1_coll_res(bool enable); +bool nfc_tag_mf1_is_use_mf1_coll_res(void); +void nfc_tag_mf1_set_write_mode(nfc_tag_mf1_write_mode_t write_mode); +nfc_tag_mf1_write_mode_t nfc_tag_mf1_get_write_mode(void); + #endif diff --git a/firmware/nrf52_sdk/components/toolchain/gcc/Makefile.posix b/firmware/nrf52_sdk/components/toolchain/gcc/Makefile.posix index 04bbd93..8066bb0 100644 --- a/firmware/nrf52_sdk/components/toolchain/gcc/Makefile.posix +++ b/firmware/nrf52_sdk/components/toolchain/gcc/Makefile.posix @@ -1,3 +1,3 @@ -GNU_INSTALL_ROOT ?= /usr/local/gcc-arm-none-eabi-10.3-2021.10/bin/ +GNU_INSTALL_ROOT ?= /usr/bin/ GNU_VERSION ?= 9.3.1 GNU_PREFIX ?= arm-none-eabi diff --git a/software/script/chameleon_cli_main.py b/software/script/chameleon_cli_main.py index bb17915..3c3e380 100755 --- a/software/script/chameleon_cli_main.py +++ b/software/script/chameleon_cli_main.py @@ -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) diff --git a/software/script/chameleon_cli_unit.py b/software/script/chameleon_cli_unit.py index 0470ce7..c5a2a60 100644 --- a/software/script/chameleon_cli_unit.py +++ b/software/script/chameleon_cli_unit.py @@ -45,10 +45,9 @@ class ArgumentParserNoExit(argparse.ArgumentParser): class BaseCLIUnit: - def __init__(self): # new a device command transfer and receiver instance(Send cmd and receive response) - self._device_com: chameleon_com.ChameleonCom = None + self._device_com: chameleon_com.ChameleonCom | None = None @property def device_com(self) -> chameleon_com.ChameleonCom: @@ -88,21 +87,18 @@ class BaseCLIUnit: raise NotImplementedError("Please implement this") @staticmethod - def sub_process(cmd, cwd=os.path.abspath("bin/"), ): + def sub_process(cmd, cwd=os.path.abspath("bin/")): class ShadowProcess: def __init__(self): self.time_start = timeit.default_timer() - self._process = subprocess.Popen( - cmd, cwd=cwd, shell=True, stderr=subprocess.PIPE, stdout=subprocess.PIPE - ) + self._process = subprocess.Popen(cmd, cwd=cwd, shell=True, stderr=subprocess.PIPE, + stdout=subprocess.PIPE) def get_time_distance(self, ms=True): - ret = 0 if ms: - ret = (timeit.default_timer() - self.time_start) * 1000 + return round((timeit.default_timer() - self.time_start) * 1000, 2) else: - ret = timeit.default_timer() - self.time_start - return round(ret, 2) + return round(timeit.default_timer() - self.time_start, 2) def is_running(self): return self._process.poll() is None @@ -195,13 +191,13 @@ class HWConnect(BaseCLIUnit): def on_exec(self, args: argparse.Namespace): try: - if args.port is None: # Chameleon Autodedect if no port is supplied + if args.port is None: # Chameleon auto-detect if no port is supplied # loop through all ports and find chameleon for port in serial.tools.list_ports.comports(): if port.vid == 0x6868: args.port = port.device break - if args.port is None: # If no chameleon was found, exit + 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.") return self.device_com.open(args.port) @@ -211,7 +207,6 @@ class HWConnect(BaseCLIUnit): class HWModeSet(DeviceRequiredUnit): - def args_parser(self) -> ArgumentParserNoExit or None: parser = ArgumentParserNoExit() help_str = "reader or r = reader mode, emulator or e = tag emulator mode." @@ -237,7 +232,6 @@ class HWModeGet(DeviceRequiredUnit): class HWChipIdGet(DeviceRequiredUnit): - def args_parser(self) -> ArgumentParserNoExit or None: return None @@ -246,15 +240,14 @@ class HWChipIdGet(DeviceRequiredUnit): class HWAddressGet(DeviceRequiredUnit): - def args_parser(self) -> ArgumentParserNoExit or None: return None def on_exec(self, args: argparse.Namespace): print(f' - Device address: ' + self.cmd_positive.get_device_address()) -class HWVersion(DeviceRequiredUnit): +class HWVersion(DeviceRequiredUnit): def args_parser(self) -> ArgumentParserNoExit or None: return None @@ -264,6 +257,7 @@ class HWVersion(DeviceRequiredUnit): git_version = self.cmd_positive.get_git_version() print(f' - Version: {fw_version} ({git_version})') + class HF14AScan(ReaderRequiredUint): def args_parser(self) -> ArgumentParserNoExit or None: pass @@ -286,7 +280,6 @@ class HF14AScan(ReaderRequiredUint): class HF14AInfo(ReaderRequiredUint): - def args_parser(self) -> ArgumentParserNoExit or None: pass @@ -316,7 +309,6 @@ class HF14AInfo(ReaderRequiredUint): class HFMFNested(ReaderRequiredUint): - def args_parser(self) -> ArgumentParserNoExit or None: type_choices = ['A', 'B', 'a', 'b'] parser = ArgumentParserNoExit() @@ -326,8 +318,7 @@ class HFMFNested(ReaderRequiredUint): 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, @@ -421,7 +412,6 @@ class HFMFNested(ReaderRequiredUint): class HFMFDarkside(ReaderRequiredUint): - def __init__(self): super().__init__() self.darkside_list = [] @@ -440,7 +430,7 @@ class HFMFDarkside(ReaderRequiredUint): retry_count = 0 while retry_count < 0xFF: darkside_resp = self.cmd_positive.acquire_darkside(block_target, type_target, first_recover, 15) - first_recover = False # not first run. + first_recover = False # not first run. darkside_obj = chameleon_cstruct.parse_darkside_acquire_result(darkside_resp.data) self.darkside_list.append(darkside_obj) recover_params = f"{darkside_obj['uid']}" @@ -462,7 +452,7 @@ class HFMFDarkside(ReaderRequiredUint): if 'key not found' in output_str: print(f" - No key found, retrying({retry_count})...") retry_count += 1 - continue # retry + continue # retry else: key_list = [] for line in output_str.split('\n'): @@ -487,7 +477,6 @@ class HFMFDarkside(ReaderRequiredUint): class BaseMF1AuthOpera(ReaderRequiredUint): - def args_parser(self) -> ArgumentParserNoExit or None: type_choices = ['A', 'B', 'a', 'b'] parser = ArgumentParserNoExit() @@ -495,8 +484,7 @@ class BaseMF1AuthOpera(ReaderRequiredUint): 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): @@ -508,6 +496,7 @@ class BaseMF1AuthOpera(ReaderRequiredUint): if not re.match(r"^[a-fA-F0-9]{12}$", key): raise ArgsParserError("key must include 12 HEX symbols") self.key: bytearray = bytearray.fromhex(key) + return Param() def on_exec(self, args: argparse.Namespace): @@ -515,7 +504,6 @@ class BaseMF1AuthOpera(ReaderRequiredUint): class HFMFRDBL(BaseMF1AuthOpera): - # hf mf rdbl -b 2 -t A -k FFFFFFFFFFFF def on_exec(self, args: argparse.Namespace): param = self.get_param(args) @@ -524,11 +512,10 @@ class HFMFRDBL(BaseMF1AuthOpera): class HFMFWRBL(BaseMF1AuthOpera): - def args_parser(self) -> ArgumentParserNoExit or None: parser = super(HFMFWRBL, self).args_parser() parser.add_argument('-d', '--data', type=str, required=True, metavar="Your block data", - help="Your block data, a hex string.") + help="Your block data, a hex string.") return parser # hf mf wrbl -b 2 -t A -k FFFFFFFFFFFF -d 00000000000000000000000000000122 @@ -545,22 +532,19 @@ 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 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'}.") + print(f" - Set mf1 detection {'enable' if enable else 'disable'}.") class HFMFDetectionLogCount(DeviceRequiredUnit): - def args_parser(self) -> ArgumentParserNoExit or None: return None @@ -572,7 +556,6 @@ class HFMFDetectionLogCount(DeviceRequiredUnit): class HFMFDetectionDecrypt(DeviceRequiredUnit): - detection_log_size = 18 def args_parser(self) -> ArgumentParserNoExit or None: @@ -654,7 +637,6 @@ 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") @@ -700,8 +682,45 @@ class HFMFELoad(DeviceRequiredUnit): print("\n - Load success") -class HFMFSim(DeviceRequiredUnit): +class HFMFSettings(DeviceRequiredUnit): + def args_parser(self) -> ArgumentParserNoExit or None: + parser = ArgumentParserNoExit() + help_str = "" + for s in chameleon_cmd.MifareClassicWriteMode: + help_str += f"{s.value} = {s}, " + help_str = help_str[:-2] + + parser.add_argument('--gen1a', type=int, required=False, help="Gen1a magic mode, 1 - enable, 0 - disable", + default=-1, choices=[1, 0]) + parser.add_argument('--gen2', type=int, required=False, help="Gen2 magic mode, 1 - enable, 0 - disable", + default=-1, choices=[1, 0]) + parser.add_argument('--coll', type=int, required=False, + help="Use anti-collision data from block 0 for 4 byte UID tags, 1 - enable, 0 - disable", + default=-1, choices=[1, 0]) + parser.add_argument('--write', type=int, required=False, + help=f"Write mode: {help_str}", + default=-1, choices=chameleon_cmd.MifareClassicWriteMode.list()) + return parser + + # hf mf settings + def on_exec(self, args: argparse.Namespace): + if args.gen1a != -1: + self.cmd_positive.set_mf1_gen1a_mode(args.gen1a) + print(f' - Set gen1a mode to {"enabled" if args.gen1a else "disabled"} success') + if args.gen2 != -1: + self.cmd_positive.set_mf1_gen2_mode(args.gen2) + print(f' - Set gen2 mode to {"enabled" if args.gen2 else "disabled"} success') + if args.coll != -1: + self.cmd_positive.set_mf1_block_anti_coll_mode(args.coll) + print(f' - Set anti-collision mode to {"enabled" if args.coll else "disabled"} success') + if args.write != -1: + self.cmd_positive.set_mf1_write_mode(args.write) + print(f' - Set write mode to {chameleon_cmd.MifareClassicWriteMode(args.write)} success') + print(f' - Emulator settings updated') + + +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") @@ -715,17 +734,17 @@ class HFMFSim(DeviceRequiredUnit): atqa_str: str = args.atqa.strip() uid_str: str = args.uid.strip() - if re.match('[a-fA-F0-9]{2}', sak_str) is not None: + if re.match(r"[a-fA-F0-9]{2}", sak_str) is not None: sak = bytearray.fromhex(sak_str) else: raise Exception("SAK must be hex(2byte)") - if re.match('[a-fA-F0-9]{4}', atqa_str) is not None: + if re.match(r"[a-fA-F0-9]{4}", atqa_str) is not None: atqa = bytearray.fromhex(atqa_str) else: raise Exception("ATQA must be hex(4byte)") - if re.match('[a-fA-F0-9]+', uid_str) is not None: + if re.match(r"[a-fA-F0-9]+", uid_str) is not None: uid_len = len(uid_str) if uid_len != 8 and uid_len != 14 and uid_len != 20: raise Exception("UID length error") @@ -738,7 +757,6 @@ class HFMFSim(DeviceRequiredUnit): class LFEMRead(ReaderRequiredUint): - def args_parser(self) -> ArgumentParserNoExit or None: return None @@ -749,7 +767,6 @@ class LFEMRead(ReaderRequiredUint): class LFEMCardRequiredUint(DeviceRequiredUnit): - @staticmethod def add_card_arg(parser: ArgumentParserNoExit): parser.add_argument("--id", type=str, required=True, help="EM410x tag id", metavar="hex") @@ -770,7 +787,6 @@ class LFEMCardRequiredUint(DeviceRequiredUnit): class LFEMWriteT55xx(LFEMCardRequiredUint, ReaderRequiredUint): - def args_parser(self) -> ArgumentParserNoExit or None: parser = ArgumentParserNoExit() return self.add_card_arg(parser) @@ -789,7 +805,6 @@ class LFEMWriteT55xx(LFEMCardRequiredUint, ReaderRequiredUint): class SlotIndexRequireUint(DeviceRequiredUnit): - def args_parser(self) -> ArgumentParserNoExit or None: raise NotImplementedError() @@ -801,12 +816,12 @@ class SlotIndexRequireUint(DeviceRequiredUnit): slot_choices = [x.value for x in chameleon_cmd.SlotNumber] help_str = f"Slot Indexes: {slot_choices}" - parser.add_argument('-s', "--slot", type=int, required=True, - help=help_str, metavar="number", choices=slot_choices) + parser.add_argument('-s', "--slot", type=int, required=True, help=help_str, metavar="number", + choices=slot_choices) return parser -class SenseTypeRequireUint(DeviceRequiredUnit): +class SenseTypeRequireUint(DeviceRequiredUnit): def args_parser(self) -> ArgumentParserNoExit or None: raise NotImplementedError() @@ -823,26 +838,55 @@ class SenseTypeRequireUint(DeviceRequiredUnit): continue help_str += f"{s.value} = {s}, " - parser.add_argument('-st', "--sense_type", type=int, required=True, - help=help_str, metavar="number", choices=sense_choices) + parser.add_argument('-st', "--sense_type", type=int, required=True, help=help_str, metavar="number", + choices=sense_choices) return parser -class HWSlotInfo(DeviceRequiredUnit): +class HWSlotList(DeviceRequiredUnit): def args_parser(self) -> ArgumentParserNoExit or None: - return + parser = ArgumentParserNoExit() + parser.add_argument('-e', '--extend', type=int, required=False, + help="Show slot nicknames and Mifare Classic emulator settings. 0 - skip, 1 - show (" + "default)", choices=[0, 1], default=1) + return parser - # hw slot info + def get_slot_name(self, slot, sense): + try: + return self.cmd_positive.get_slot_tag_nick_name(slot, sense).data.decode() + except chameleon_cmd.NegativeResponseError: + return "Empty" + except UnicodeDecodeError: + return "Non UTF-8" + + # hw slot list def on_exec(self, args: argparse.Namespace): data = self.cmd_positive.get_slot_info().data + enabled = self.cmd_positive.get_enabled_slots().data selected = chameleon_cmd.SlotNumber.from_fw(self.cmd_positive.get_active_slot().data[0]) for slot in chameleon_cmd.SlotNumber: - print(f' - Slot {slot} data{" (active)" if slot == selected else ""}:') - print(f' HF: {chameleon_cmd.TagSpecificType(data[chameleon_cmd.SlotNumber.to_fw(slot) * 2])}') - print(f' LF: {chameleon_cmd.TagSpecificType(data[chameleon_cmd.SlotNumber.to_fw(slot) * 2 + 1])}') + print( + f' - Slot {slot} data{" (active)" if slot == selected else ""}' + f'{" (disabled)" if not enabled[chameleon_cmd.SlotNumber.to_fw(slot)] else ""}:') + print( + f' HF: ' + f'{(self.get_slot_name(slot, chameleon_cmd.TagSenseType.TAG_SENSE_HF) + " - ") if args.extend else ""}' + f'{chameleon_cmd.TagSpecificType(data[chameleon_cmd.SlotNumber.to_fw(slot) * 2])}') + print( + f' LF: ' + f'{(self.get_slot_name(slot, chameleon_cmd.TagSenseType.TAG_SENSE_LF) + " - ") if args.extend else ""}' + f'{chameleon_cmd.TagSpecificType(data[chameleon_cmd.SlotNumber.to_fw(slot) * 2 + 1])}') + if args.extend: + config = self.cmd_positive.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])}') + class HWSlotSet(SlotIndexRequireUint): - def args_parser(self) -> ArgumentParserNoExit or None: parser = ArgumentParserNoExit() return self.add_slot_args(parser) @@ -855,7 +899,6 @@ class HWSlotSet(SlotIndexRequireUint): class TagTypeRequiredUint(DeviceRequiredUnit): - @staticmethod def add_type_args(parser: ArgumentParserNoExit): type_choices = chameleon_cmd.TagSpecificType.list() @@ -864,8 +907,9 @@ class TagTypeRequiredUint(DeviceRequiredUnit): if t == chameleon_cmd.TagSpecificType.TAG_TYPE_UNKNOWN: continue help_str += f"{t.value} = {t}, " - parser.add_argument('-t', "--type", type=int, required=True, help=help_str, - metavar="number", choices=type_choices) + help_str = help_str[:-2] + parser.add_argument('-t', "--type", type=int, required=True, help=help_str, metavar="number", + choices=type_choices) return parser def args_parser(self) -> ArgumentParserNoExit or None: @@ -876,7 +920,6 @@ class TagTypeRequiredUint(DeviceRequiredUnit): class HWSlotTagType(TagTypeRequiredUint, SlotIndexRequireUint): - def args_parser(self) -> ArgumentParserNoExit or None: parser = ArgumentParserNoExit() self.add_type_args(parser) @@ -892,7 +935,6 @@ class HWSlotTagType(TagTypeRequiredUint, SlotIndexRequireUint): class HWSlotDataDefault(TagTypeRequiredUint, SlotIndexRequireUint): - def args_parser(self) -> ArgumentParserNoExit or None: parser = ArgumentParserNoExit() self.add_type_args(parser) @@ -924,7 +966,6 @@ class HWSlotEnableSet(SlotIndexRequireUint): class LFEMSim(LFEMCardRequiredUint): - def args_parser(self) -> ArgumentParserNoExit or None: parser = ArgumentParserNoExit() return self.add_card_arg(parser) @@ -969,11 +1010,10 @@ class HWSlotNickGet(SlotIndexRequireUint, SenseTypeRequireUint): slot_num = args.slot sense_type = args.sense_type res = self.cmd_positive.get_slot_tag_nick_name(slot_num, sense_type) - print(f' - Get tag nick name for slot {slot_num}: {res.data.decode(encoding="utf8")}') + print(f' - Get tag nick name for slot {slot_num}: {res.data.decode()}') class HWSlotUpdate(DeviceRequiredUnit): - def args_parser(self) -> ArgumentParserNoExit or None: return None @@ -984,7 +1024,6 @@ class HWSlotUpdate(DeviceRequiredUnit): class HWSlotOpenAll(DeviceRequiredUnit): - def args_parser(self) -> ArgumentParserNoExit or None: return None @@ -1013,7 +1052,6 @@ class HWSlotOpenAll(DeviceRequiredUnit): class HWDFU(DeviceRequiredUnit): - def args_parser(self) -> ArgumentParserNoExit or None: return None @@ -1028,9 +1066,11 @@ class HWDFU(DeviceRequiredUnit): # let time for comm thread to send dfu cmd and close port time.sleep(0.1) + class HWSettingsAnimationGet(DeviceRequiredUnit): def args_parser(self) -> ArgumentParserNoExit or None: return None + def on_exec(self, args: argparse.Namespace): resp: chameleon_com.Response = self.cmd_standard.get_settings_animation() if resp.data[0] == 0: @@ -1042,22 +1082,25 @@ class HWSettingsAnimationGet(DeviceRequiredUnit): else: print("Unknown setting value, something failed.") + class HWSettingsAnimationSet(DeviceRequiredUnit): def args_parser(self) -> ArgumentParserNoExit or None: parser = ArgumentParserNoExit() - parser.add_argument('-m', '--mode', type=int, required=True, help="0 is full (default), 1 is minimal (only single pass on button wakeup), 2 is none", choices=[0, 1, 2]) + parser.add_argument('-m', '--mode', type=int, required=True, + help="0 is full (default), 1 is minimal (only single pass on button wakeup), 2 is none", + choices=[0, 1, 2]) return parser - + def on_exec(self, args: argparse.Namespace): mode = args.mode self.cmd_standard.set_settings_animation(mode) print("Animation mode change success. Do not forget to store your settings in flash!") - + class HWSettingsStore(DeviceRequiredUnit): def args_parser(self) -> ArgumentParserNoExit or None: return None - + def on_exec(self, args: argparse.Namespace): print("Storing settings...") resp: chameleon_com.Response = self.cmd_standard.store_settings() @@ -1066,6 +1109,7 @@ class HWSettingsStore(DeviceRequiredUnit): else: print(" - Store failed") + class HWSettingsReset(DeviceRequiredUnit): def args_parser(self) -> ArgumentParserNoExit or None: return None @@ -1078,19 +1122,16 @@ class HWSettingsReset(DeviceRequiredUnit): else: print(" - Reset failed") + class HWFactoryReset(DeviceRequiredUnit): def args_parser(self) -> ArgumentParserNoExit: parser = ArgumentParserNoExit() 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 :)" - ) + "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 :)") return parser + def on_exec(self, args: argparse.Namespace): if not args.i_know_what_im_doing: print("This time your data's safe. Read the command documentation next time.") diff --git a/software/script/chameleon_cmd.py b/software/script/chameleon_cmd.py index 669b4eb..8db0db3 100644 --- a/software/script/chameleon_cmd.py +++ b/software/script/chameleon_cmd.py @@ -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 diff --git a/software/script/chameleon_com.py b/software/script/chameleon_com.py index a58a8f9..92b5977 100644 --- a/software/script/chameleon_com.py +++ b/software/script/chameleon_com.py @@ -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) diff --git a/software/script/chameleon_cstruct.py b/software/script/chameleon_cstruct.py index eb826ed..519cdc8 100644 --- a/software/script/chameleon_cstruct.py +++ b/software/script/chameleon_cstruct.py @@ -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 - - diff --git a/software/script/chameleon_status.py b/software/script/chameleon_status.py index 675aaea..63c8579 100644 --- a/software/script/chameleon_status.py +++ b/software/script/chameleon_status.py @@ -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" }