diff --git a/firmware/application/src/app_cmd.c b/firmware/application/src/app_cmd.c index 2bbbdbd..c2da08d 100644 --- a/firmware/application/src/app_cmd.c +++ b/firmware/application/src/app_cmd.c @@ -377,6 +377,14 @@ data_frame_tx_t* cmd_processor_set_em410x_emu_id(uint16_t cmd, uint16_t status, return data_frame_make(cmd, status, 0, NULL); } +data_frame_tx_t* cmd_processor_get_em410x_emu_id(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) { + tag_data_buffer_t* buffer = get_buffer_by_tag_type(TAG_TYPE_EM410X); + uint8_t responseData[LF_EM410X_TAG_ID_SIZE]; + memcpy(responseData, buffer->buffer, LF_EM410X_TAG_ID_SIZE); + status = STATUS_DEVICE_SUCCESS; + return data_frame_make(cmd, status, LF_EM410X_TAG_ID_SIZE, responseData); +} + data_frame_tx_t* cmd_processor_set_mf1_detection_enable(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) { if (length == 1 && (data[0] == 0 || data[0] == 1)) { nfc_tag_mf1_detection_log_clear(); @@ -456,6 +464,33 @@ data_frame_tx_t* cmd_processor_set_mf1_emulator_block(uint16_t cmd, uint16_t sta return data_frame_make(cmd, status, 0, NULL); } +data_frame_tx_t* cmd_processor_get_mf1_emulator_block(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) { + if (length == 3) { + uint8_t block_index = data[0]; + uint16_t block_count = data[1] | (data[2] << 8); + if (block_count == 0 || block_index + block_count > NFC_TAG_MF1_BLOCK_MAX) { + status = STATUS_PAR_ERR; + } + else { + tag_data_buffer_t* buffer = get_buffer_by_tag_type(TAG_TYPE_MIFARE_4096); + nfc_tag_mf1_information_t *info = (nfc_tag_mf1_information_t *)buffer->buffer; + uint16_t result_length = block_count * NFC_TAG_MF1_DATA_SIZE; + uint8_t result_buffer[result_length]; + for (int i = 0, j = block_index; i < result_length; i += NFC_TAG_MF1_DATA_SIZE, j++) { + uint8_t *p_block = &result_buffer[i]; + memcpy(p_block, info->memory[j], NFC_TAG_MF1_DATA_SIZE); + } + + return data_frame_make(cmd, status, result_length, result_buffer); + } + } + else { + status = STATUS_PAR_ERR; + } + + return data_frame_make(cmd, status, 0, NULL); +} + data_frame_tx_t* cmd_processor_set_mf1_anti_collision_res(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) { if (length > 13) { // sak(1) + atqa(2) + uid(10) @@ -463,7 +498,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 +563,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,16 +767,29 @@ 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 }, + { DATA_CMD_GET_EM410X_EMU_ID, NULL, cmd_processor_get_em410x_emu_id, NULL }, { DATA_CMD_GET_MF1_DETECTION_STATUS, NULL, cmd_processor_get_mf1_detection_status, NULL }, { DATA_CMD_SET_MF1_DETECTION_ENABLE, NULL, cmd_processor_set_mf1_detection_enable, NULL }, { DATA_CMD_GET_MF1_DETECTION_COUNT, NULL, cmd_processor_get_mf1_detection_count, NULL }, { 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_LOAD_MF1_EMU_BLOCK_DATA, NULL, cmd_processor_set_mf1_emulator_block, NULL }, + { DATA_CMD_READ_MF1_EMU_BLOCK_DATA, NULL, cmd_processor_get_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..a1bf6e7 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) // // ****************************************************************** @@ -67,8 +68,24 @@ // Range from 4000 -> 4999 // ****************************************************************** // -#define DATA_CMD_LOAD_MF1_BLOCK_DATA (4000) +#define DATA_CMD_LOAD_MF1_EMU_BLOCK_DATA (4000) #define DATA_CMD_SET_MF1_ANTI_COLLISION_RES (4001) +#define DATA_CMD_SET_MF1_ANTICOLLISION_INFO (4002) +#define DATA_CMD_SET_MF1_ATS_RESOURCE (4003) +#define DATA_CMD_SET_MF1_DETECTION_ENABLE (4004) +#define DATA_CMD_GET_MF1_DETECTION_COUNT (4005) +#define DATA_CMD_GET_MF1_DETECTION_RESULT (4006) +#define DATA_CMD_GET_MF1_DETECTION_STATUS (4007) +#define DATA_CMD_READ_MF1_EMU_BLOCK_DATA (4008) +#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) // // ****************************************************************** @@ -82,11 +99,6 @@ // // ****************************************************************** #define DATA_CMD_SET_EM410X_EMU_ID (5000) -#define DATA_CMD_SET_MF1_ANTICOLLISION_INFO (5001) -#define DATA_CMD_SET_MF1_ATS_RESOURCE (5002) -#define DATA_CMD_SET_MF1_DETECTION_ENABLE (5003) -#define DATA_CMD_GET_MF1_DETECTION_COUNT (5004) -#define DATA_CMD_GET_MF1_DETECTION_RESULT (5005) -#define DATA_CMD_GET_MF1_DETECTION_STATUS (5006) +#define DATA_CMD_GET_EM410X_EMU_ID (5001) #endif 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/firmware/nrf52_sdk/modules/nrfx/drivers/src/nrfx_nfct.c b/firmware/nrf52_sdk/modules/nrfx/drivers/src/nrfx_nfct.c index b345c09..f1741dd 100644 --- a/firmware/nrf52_sdk/modules/nrfx/drivers/src/nrfx_nfct.c +++ b/firmware/nrf52_sdk/modules/nrfx/drivers/src/nrfx_nfct.c @@ -41,6 +41,8 @@ #include #if NRFX_CHECK(NRFX_NFCT_ENABLED) +// ChameleonUltra: workaround because NFC IRQ gets repetitively called when in HF field once a comm has started +#include "bsp_wdt.h" #include @@ -907,6 +909,8 @@ void nrfx_nfct_irq_handler(void) m_nfct_cb.config.cb(&nfct_evt); } } + // ChameleonUltra: workaround because NFC IRQ gets repetitively called when in HF field once a comm has started + bsp_wdt_feed(); } #endif // NRFX_CHECK(NRFX_NFCT_ENABLED) diff --git a/software/script/chameleon_cli_main.py b/software/script/chameleon_cli_main.py index 16cacbb..013a0d3 100755 --- a/software/script/chameleon_cli_main.py +++ b/software/script/chameleon_cli_main.py @@ -1,6 +1,5 @@ #!/usr/bin/env python3 import argparse -import os import platform import sys import traceback diff --git a/software/script/chameleon_cli_unit.py b/software/script/chameleon_cli_unit.py index ec080d2..20cb3f2 100644 --- a/software/script/chameleon_cli_unit.py +++ b/software/script/chameleon_cli_unit.py @@ -1,3 +1,4 @@ +import binascii import os import re import subprocess @@ -19,7 +20,7 @@ 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: @@ -55,21 +56,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 @@ -152,7 +150,7 @@ class ReaderRequiredUnit(DeviceRequiredUnit): hw = CLITree('hw', 'hardware controller') -hw_chipid = hw.subgroup('chipid', 'Device chipsed ID get') +hw_chipid = hw.subgroup('chipid', 'Device chipset ID get') hw_address = hw.subgroup('address', 'Device address get') hw_mode = hw.subgroup('mode', 'Device mode get/set') hw_slot = hw.subgroup('slot', 'Emulation tag slot.') @@ -168,6 +166,7 @@ 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') root_commands: dict[str, CLITree] = { 'hw': hw, @@ -187,13 +186,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) @@ -204,7 +203,6 @@ class HWConnect(BaseCLIUnit): @hw_mode.command('set', 'Change device mode to tag reader or tag emulator') 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." @@ -232,7 +230,6 @@ class HWModeGet(DeviceRequiredUnit): @hw_chipid.command('get', 'Get device chipset ID') class HWChipIdGet(DeviceRequiredUnit): - def args_parser(self) -> ArgumentParserNoExit or None: return None @@ -242,7 +239,6 @@ class HWChipIdGet(DeviceRequiredUnit): @hw_address.command('get', 'Get device address (used with Bluetooth)') class HWAddressGet(DeviceRequiredUnit): - def args_parser(self) -> ArgumentParserNoExit or None: return None @@ -252,7 +248,6 @@ class HWAddressGet(DeviceRequiredUnit): @hw.command('version', 'Get current device firmware version') class HWVersion(DeviceRequiredUnit): - def args_parser(self) -> ArgumentParserNoExit or None: return None @@ -328,8 +323,7 @@ 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, @@ -465,7 +459,7 @@ class HFMFDarkside(ReaderRequiredUnit): 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'): @@ -498,8 +492,7 @@ 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): @@ -511,15 +504,15 @@ class BaseMF1AuthOpera(ReaderRequiredUnit): 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): raise NotImplementedError("Please implement this") -@hf_mf.command('rdbl', 'MiFARE Classic read one block') +@hf_mf.command('rdbl', 'Mifare Classic read one block') class HFMFRDBL(BaseMF1AuthOpera): - # hf mf rdbl -b 2 -t A -k FFFFFFFFFFFF def on_exec(self, args: argparse.Namespace): param = self.get_param(args) @@ -527,13 +520,12 @@ class HFMFRDBL(BaseMF1AuthOpera): print(f" - Data: {resp.data.hex()}") -@hf_mf.command('wrbl', 'MiFARE Classic write one block') +@hf_mf.command('wrbl', 'Mifare Classic write one block') 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 @@ -551,11 +543,9 @@ class HFMFWRBL(BaseMF1AuthOpera): @hf_mf_detection.command('enable', 'Detection enable') 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 @@ -567,7 +557,6 @@ class HFMFDetectionEnable(DeviceRequiredUnit): @hf_mf_detection.command('count', 'Detection log count') class HFMFDetectionLogCount(DeviceRequiredUnit): - def args_parser(self) -> ArgumentParserNoExit or None: return None @@ -580,7 +569,6 @@ class HFMFDetectionLogCount(DeviceRequiredUnit): @hf_mf_detection.command('decrypt', 'Download log and decrypt keys') class HFMFDetectionDecrypt(DeviceRequiredUnit): - detection_log_size = 18 def args_parser(self) -> ArgumentParserNoExit or None: @@ -663,7 +651,6 @@ class HFMFDetectionDecrypt(DeviceRequiredUnit): @hf_mf.command('eload', 'Load data to emulator memory') class HFMFELoad(DeviceRequiredUnit): - def args_parser(self) -> ArgumentParserNoExit or None: parser = ArgumentParserNoExit() parser.add_argument('-f', '--file', type=str, required=True, help="file path") @@ -709,9 +696,97 @@ class HFMFELoad(DeviceRequiredUnit): print("\n - Load success") -@hf_mf.command('sim', 'Simulation a mifare classic card') -class HFMFSim(DeviceRequiredUnit): +@hf_mf.command('eread', 'Read data from emulator memory') +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']) + return parser + def on_exec(self, args: argparse.Namespace): + file = args.file + if args.type is None: + if file.endswith('.bin'): + content_type = 'bin' + elif file.endswith('.eml'): + content_type = 'hex' + else: + raise Exception("Unknown file format, Specify content type with -t option") + else: + content_type = args.type + + selected_slot = self.cmd.get_active_slot().data[0] + slot_info = self.cmd.get_slot_info().data + tag_type = chameleon_cmd.TagSpecificType(slot_info[selected_slot * 2]) + if tag_type == chameleon_cmd.TagSpecificType.TAG_TYPE_MIFARE_Mini: + block_count = 20 + elif tag_type == chameleon_cmd.TagSpecificType.TAG_TYPE_MIFARE_1024: + block_count = 64 + elif tag_type == chameleon_cmd.TagSpecificType.TAG_TYPE_MIFARE_2048: + block_count = 128 + 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") + + with open(file, 'wb') as fd: + block = 0 + while block < block_count: + response = self.cmd.get_mf1_block_data(block, 1) + print('.', end='') + block += 1 + if content_type == 'hex': + hex_char_repr = binascii.hexlify(response.data) + fd.write(hex_char_repr) + fd.write(bytes([0x0a])) + else: + fd.write(response.data) + + print("\n - Read success") + + +@hf_mf.command('settings', 'Settings of Mifare Classic emulator') +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.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.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.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.set_mf1_write_mode(args.write) + print(f' - Set write mode to {chameleon_cmd.MifareClassicWriteMode(args.write)} success') + print(f' - Emulator settings updated') + + +@hf_mf.command('sim', 'Simulate a Mifare Classic card') +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") @@ -725,17 +800,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") @@ -813,13 +888,12 @@ class SlotIndexRequireUnit(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 SenseTypeRequireUnit(DeviceRequiredUnit): - def args_parser(self) -> ArgumentParserNoExit or None: raise NotImplementedError() @@ -836,24 +910,53 @@ class SenseTypeRequireUnit(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 -@hw_slot.command('info', 'Get information about slots') -class HWSlotInfo(DeviceRequiredUnit): +@hw_slot.command('list', 'Get information about slots') +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.get_slot_tag_nick_name(slot, sense).data.decode() + except UnexpectedResponseError: + return "Empty" + except UnicodeDecodeError: + return "Non UTF-8" + + # 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]) + enabled = self.cmd.get_enabled_slots().data 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.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])}') @hw_slot.command('change', 'Set emulation tag slot activated.') @@ -880,8 +983,9 @@ class TagTypeRequiredUnit(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: @@ -942,21 +1046,34 @@ class HWSlotEnableSet(SlotIndexRequireUnit): print(f' - Set slot {slot_num} {"enable" if enable else "disable"} success.') -@lf_em.command('sim', 'Simulation a em410x id card') -class LFEMSim(LFEMCardRequiredUnit): +@lf_em_sim.command('set', 'Set simulated em410x card id') +class LFEMSimSet(LFEMCardRequiredUnit): def args_parser(self) -> ArgumentParserNoExit or None: parser = ArgumentParserNoExit() return self.add_card_arg(parser) - # lf em sim --id 4545454545 + # lf em sim set --id 4545454545 def on_exec(self, args: argparse.Namespace): id_hex = args.id id_bytes = bytearray.fromhex(id_hex) - self.cmd.set_em140x_sim_id(id_bytes) + self.cmd.set_em410x_sim_id(id_bytes) print(f' - Set em410x tag id success.') +@lf_em_sim.command('get', 'Get simulated em410x card id') +class LFEMSimGet(DeviceRequiredUnit): + + def args_parser(self) -> ArgumentParserNoExit or None: + return None + + # lf em sim get + def on_exec(self, args: argparse.Namespace): + response = self.cmd.get_em410x_sim_id() + print(f' - Get em410x tag id success.') + print(f'ID: {response.data.hex()}') + + @hw_slot_nick.command('set', 'Set tag nick name for slot') class HWSlotNickSet(SlotIndexRequireUnit, SenseTypeRequireUnit): def args_parser(self) -> ArgumentParserNoExit or None: @@ -991,12 +1108,11 @@ class HWSlotNickGet(SlotIndexRequireUnit, SenseTypeRequireUnit): slot_num = args.slot sense_type = args.sense_type res = self.cmd.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()}') @hw_slot.command('update', 'Update config & data to device flash') class HWSlotUpdate(DeviceRequiredUnit): - def args_parser(self) -> ArgumentParserNoExit or None: return None @@ -1008,7 +1124,6 @@ class HWSlotUpdate(DeviceRequiredUnit): @hw_slot.command('openall', 'Open all slot and set to default data') class HWSlotOpenAll(DeviceRequiredUnit): - def args_parser(self) -> ArgumentParserNoExit or None: return None @@ -1038,7 +1153,6 @@ class HWSlotOpenAll(DeviceRequiredUnit): @hw.command('dfu', 'Restart application to bootloader mode(Not yet implement dfu).') class HWDFU(DeviceRequiredUnit): - def args_parser(self) -> ArgumentParserNoExit or None: return None @@ -1058,6 +1172,7 @@ class HWDFU(DeviceRequiredUnit): 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.get_settings_animation() if resp.data[0] == 0: @@ -1074,20 +1189,22 @@ class HWSettingsAnimationGet(DeviceRequiredUnit): 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.set_settings_animation(mode) print("Animation mode change success. Do not forget to store your settings in flash!") - + @hw_settings.command('store', 'Store current settings to 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.store_settings() @@ -1116,15 +1233,11 @@ 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 c5089d4..a851ea6 100644 --- a/software/script/chameleon_cmd.py +++ b/software/script/chameleon_cmd.py @@ -1,4 +1,5 @@ import enum +import struct import chameleon_com import chameleon_status @@ -33,6 +34,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 @@ -47,13 +50,28 @@ DATA_CMD_MF1_WRITE_ONE_BLOCK = 2009 DATA_CMD_SCAN_EM410X_TAG = 3000 DATA_CMD_WRITE_EM410X_TO_T5577 = 3001 -DATA_CMD_LOAD_MF1_BLOCK_DATA = 4000 +DATA_CMD_LOAD_MF1_EMU_BLOCK_DATA = 4000 DATA_CMD_SET_MF1_ANTI_COLLISION_RES = 4001 +DATA_CMD_SET_MF1_DETECTION_ENABLE = 4004 +DATA_CMD_GET_MF1_DETECTION_COUNT = 4005 +DATA_CMD_GET_MF1_DETECTION_RESULT = 4006 + +DATA_CMD_READ_MF1_EMU_BLOCK_DATA = 4008 + +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 +DATA_CMD_GET_EM410X_EMU_ID = 5001 + @enum.unique class SlotNumber(enum.IntEnum): @@ -67,7 +85,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 @@ -79,13 +97,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): @@ -101,18 +118,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 @@ -144,6 +162,33 @@ 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 ChameleonCMD: """ Chameleon cmd function @@ -159,25 +204,25 @@ class ChameleonCMD: """ 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 +230,7 @@ class ChameleonCMD: 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): @@ -202,21 +247,21 @@ class ChameleonCMD: 扫描场内的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): """ @@ -326,7 +371,7 @@ class ChameleonCMD: 读取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) @expect_response(chameleon_status.Device.LF_TAG_OK) def write_em_410x_to_t55xx(self, id_bytes: bytearray): @@ -336,10 +381,7 @@ class ChameleonCMD: :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() @@ -354,20 +396,20 @@ class ChameleonCMD: 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) @expect_response(chameleon_status.Device.STATUS_DEVICE_SUCCESS) 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 @@ -420,7 +462,7 @@ class ChameleonCMD: return self.device.send_cmd_sync(DATA_CMD_SET_SLOT_ENABLE, 0X00, data) @expect_response(chameleon_status.Device.STATUS_DEVICE_SUCCESS) - def set_em140x_sim_id(self, id_bytes: bytearray): + def set_em410x_sim_id(self, id_bytes: bytearray): """ 设置EM410x模拟的卡号 :param id_bytes: 卡号的字节 @@ -429,6 +471,12 @@ class ChameleonCMD: if len(id_bytes) != 5: raise ValueError("The id bytes length must equal 5") return self.device.send_cmd_sync(DATA_CMD_SET_EM410X_EMU_ID, 0x00, id_bytes) + + def get_em410x_sim_id(self): + """ + Get the simulated EM410x card id + """ + return self.device.send_cmd_sync(DATA_CMD_GET_EM410X_EMU_ID, 0x00) @expect_response(chameleon_status.Device.STATUS_DEVICE_SUCCESS) def set_mf1_detection_enable(self, enable: bool): @@ -446,7 +494,7 @@ class ChameleonCMD: 获取当前侦测记录的统计个数 :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) @expect_response(chameleon_status.Device.STATUS_DEVICE_SUCCESS) def get_mf1_detection_log(self, index: int): @@ -470,7 +518,14 @@ class ChameleonCMD: data = bytearray() data.append(block_start & 0xFF) data.extend(block_data) - return self.device.send_cmd_sync(DATA_CMD_LOAD_MF1_BLOCK_DATA, 0x00, data) + return self.device.send_cmd_sync(DATA_CMD_LOAD_MF1_EMU_BLOCK_DATA, 0x00, data) + + def get_mf1_block_data(self, block_start: int, block_count: int): + """ + Gets data for selected block range + """ + data = struct.pack('