From c4abd26742eec374d2a4d655e6f43e37b8db49ba Mon Sep 17 00:00:00 2001 From: turbocool3r Date: Tue, 9 Jul 2024 01:55:06 +0300 Subject: [PATCH] Add `hf mfu ercnt/ewcnt` commands for reading and writing emulator's counters. --- firmware/application/src/app_cmd.c | 36 ++++++++++++++++++ firmware/application/src/data_cmd.h | 2 + .../src/rfid/nfctag/hf/nfc_mf0_ntag.c | 32 ++++++---------- .../src/rfid/nfctag/hf/nfc_mf0_ntag.h | 9 +++++ software/script/chameleon_cli_unit.py | 37 +++++++++++++++++++ software/script/chameleon_cmd.py | 20 ++++++++++ software/script/chameleon_enum.py | 2 + 7 files changed, 118 insertions(+), 20 deletions(-) diff --git a/firmware/application/src/app_cmd.c b/firmware/application/src/app_cmd.c index 947b165f..6820f2e7 100644 --- a/firmware/application/src/app_cmd.c +++ b/firmware/application/src/app_cmd.c @@ -893,6 +893,40 @@ static data_frame_tx_t *cmd_processor_mf0_ntag_set_signature_data(uint16_t cmd, return data_frame_make(cmd, STATUS_SUCCESS, 0, NULL); } +static data_frame_tx_t *cmd_processor_mf0_ntag_get_counter_data(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) { + if (length != 1) return data_frame_make(cmd, STATUS_INVALID_PARAMS, 0, NULL); + + uint8_t index = data[0] & 0x7F; + uint8_t *counter_data = nfc_tag_mf0_ntag_get_counter_data_by_index(index); + if (counter_data == NULL) return data_frame_make(cmd, STATUS_INVALID_SLOT_TYPE, 0, NULL); + + bool tearing = (counter_data[MF0_NTAG_AUTHLIM_OFF_IN_CTR] & MF0_NTAG_AUTHLIM_MASK_IN_CTR) != 0; + + uint8_t response[4]; + memcpy(response, counter_data, 3); + response[3] = tearing ? 0x00 : 0xBD; + + return data_frame_make(cmd, STATUS_SUCCESS, 4, response); +} + +static data_frame_tx_t *cmd_processor_mf0_ntag_set_counter_data(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) { + if (length != 4) return data_frame_make(cmd, STATUS_INVALID_PARAMS, 0, NULL); + + uint8_t index = data[0] & 0x7F; + uint8_t *counter_data = nfc_tag_mf0_ntag_get_counter_data_by_index(index); + if (counter_data == NULL) return data_frame_make(cmd, STATUS_INVALID_SLOT_TYPE, 0, NULL); + + // clear tearing event flag + if ((data[0] & 0x80) == 0x80) { + counter_data[MF0_NTAG_AUTHLIM_OFF_IN_CTR] &= ~MF0_NTAG_TEARING_MASK_IN_AUTHLIM; + } + + // copy the actual counter value + memcpy(counter_data, &data[1], 3); + + return data_frame_make(cmd, STATUS_SUCCESS, 0, NULL); +} + static data_frame_tx_t *cmd_processor_hf14a_set_anti_coll_data(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) { // uidlen[1]|uid[uidlen]|atqa[2]|sak[1]|atslen[1]|ats[atslen] // dynamic length, so no struct @@ -1238,6 +1272,8 @@ static cmd_data_map_t m_data_cmd_map[] = { { DATA_CMD_MF0_NTAG_SET_VERSION_DATA, NULL, cmd_processor_mf0_ntag_set_version_data, NULL }, { DATA_CMD_MF0_NTAG_GET_SIGNATURE_DATA, NULL, cmd_processor_mf0_ntag_get_signature_data, NULL }, { DATA_CMD_MF0_NTAG_SET_SIGNATURE_DATA, NULL, cmd_processor_mf0_ntag_set_signature_data, NULL }, + { DATA_CMD_MF0_NTAG_GET_COUNTER_DATA, NULL, cmd_processor_mf0_ntag_get_counter_data, NULL }, + { DATA_CMD_MF0_NTAG_SET_COUNTER_DATA, NULL, cmd_processor_mf0_ntag_set_counter_data, NULL }, { DATA_CMD_EM410X_SET_EMU_ID, NULL, cmd_processor_em410x_set_emu_id, NULL }, { DATA_CMD_EM410X_GET_EMU_ID, NULL, cmd_processor_em410x_get_emu_id, NULL }, diff --git a/firmware/application/src/data_cmd.h b/firmware/application/src/data_cmd.h index fae143ac..2000d476 100644 --- a/firmware/application/src/data_cmd.h +++ b/firmware/application/src/data_cmd.h @@ -114,6 +114,8 @@ #define DATA_CMD_MF0_NTAG_SET_VERSION_DATA (4024) #define DATA_CMD_MF0_NTAG_GET_SIGNATURE_DATA (4025) #define DATA_CMD_MF0_NTAG_SET_SIGNATURE_DATA (4026) +#define DATA_CMD_MF0_NTAG_GET_COUNTER_DATA (4027) +#define DATA_CMD_MF0_NTAG_SET_COUNTER_DATA (4028) // // ****************************************************************** diff --git a/firmware/application/src/rfid/nfctag/hf/nfc_mf0_ntag.c b/firmware/application/src/rfid/nfctag/hf/nfc_mf0_ntag.c index 0c37d374..8adff231 100644 --- a/firmware/application/src/rfid/nfctag/hf/nfc_mf0_ntag.c +++ b/firmware/application/src/rfid/nfctag/hf/nfc_mf0_ntag.c @@ -102,14 +102,6 @@ NRF_LOG_MODULE_REGISTER(); #define SIGNATURE_LENGTH 32 #define PAGES_PER_VERSION (NFC_TAG_MF0_NTAG_VER_SIZE / NFC_TAG_MF0_NTAG_DATA_SIZE) -// Since all counters are 24-bit and each currently supported tag that supports counters -// has password authentication we store the auth attempts counter in the last bit of the -// first counter. AUTHLIM is only 3 bits though so we reserve 4 bits just to be sure and -// use the top bit for tearing event flag. -#define AUTHLIM_OFF_IN_CTR 3 -#define AUTHLIM_MASK_IN_CTR 0xF -#define TEARING_MASK_IN_AUTHLIM 0x80 - // Values for MIRROR_CONF #define MIRROR_CONF_DISABLED 0 #define MIRROR_CONF_UID 1 @@ -403,7 +395,7 @@ static int get_user_data_end_by_tag_type(tag_specific_type_t type) { return nr_pages; } -static uint8_t *get_counter_data_by_index(uint8_t index) { +uint8_t *nfc_tag_mf0_ntag_get_counter_data_by_index(uint8_t index) { uint8_t ctr_page_off; uint8_t ctr_page_end; switch (m_tag_type) { @@ -489,12 +481,12 @@ static void handle_any_read(uint8_t block_num, uint8_t block_cnt, uint8_t block_ bytes2hex(m_tag_information->res_coll.uid, (char *)mirror_buf, 7); break; case MIRROR_CONF_CNT: - bytes2hex(get_counter_data_by_index(0), (char *)mirror_buf, 3); + bytes2hex(nfc_tag_mf0_ntag_get_counter_data_by_index(0), (char *)mirror_buf, 3); break; case MIRROR_CONF_UID_CNT: bytes2hex(m_tag_information->res_coll.uid, (char *)mirror_buf, 7); mirror_buf[7] = 'x'; - bytes2hex(get_counter_data_by_index(0), (char *)&mirror_buf[8], 3); + bytes2hex(nfc_tag_mf0_ntag_get_counter_data_by_index(0), (char *)&mirror_buf[8], 3); break; } } @@ -546,7 +538,7 @@ static void handle_any_read(uint8_t block_num, uint8_t block_cnt, uint8_t block_ int access = m_tag_information->memory[first_cfg_page + CONF_ACCESS_PAGE_OFFSET][CONF_ACCESS_BYTE]; if ((access & CONF_ACCESS_NFC_CNT_EN) != 0) { - uint8_t *ctr = get_counter_data_by_index(0); + uint8_t *ctr = nfc_tag_mf0_ntag_get_counter_data_by_index(0); uint32_t counter = (((uint32_t)ctr[0]) << 16) | (((uint32_t)ctr[1]) << 8) | ((uint32_t)ctr[2]); if (counter < 0xFFFFFF) counter += 1; ctr[0] = (uint8_t)(counter >> 16); @@ -755,7 +747,7 @@ static void handle_read_cnt_command(uint8_t index) { } } - uint8_t *cnt_data = get_counter_data_by_index(index); + uint8_t *cnt_data = nfc_tag_mf0_ntag_get_counter_data_by_index(index); if (cnt_data == NULL) { nfc_tag_14a_tx_nbit(NAK_INVALID_OPERATION_TBIV, 4); return; @@ -794,7 +786,7 @@ static void handle_incr_cnt_command(uint8_t block_num, uint8_t *p_data) { if ((0xFFFFFF - cnt) < incr_value) { // set tearing event flag - cnt_data[AUTHLIM_OFF_IN_CTR] |= TEARING_MASK_IN_AUTHLIM; + cnt_data[MF0_NTAG_AUTHLIM_OFF_IN_CTR] |= MF0_NTAG_TEARING_MASK_IN_AUTHLIM; nfc_tag_14a_tx_nbit(NAK_INVALID_OPERATION_TBIV, 4); } else { @@ -810,14 +802,14 @@ static void handle_incr_cnt_command(uint8_t block_num, uint8_t *p_data) { static void handle_pwd_auth_command(uint8_t *p_data) { int first_cfg_page = get_first_cfg_page_by_tag_type(m_tag_type); - uint8_t *cnt_data = get_counter_data_by_index(0); + uint8_t *cnt_data = nfc_tag_mf0_ntag_get_counter_data_by_index(0); if (first_cfg_page == 0 || cnt_data == NULL) { nfc_tag_14a_tx_nbit(NAK_INVALID_OPERATION_TBIV, 4); return; } // check AUTHLIM counter - uint8_t auth_cnt = cnt_data[AUTHLIM_OFF_IN_CTR] & AUTHLIM_MASK_IN_CTR; + uint8_t auth_cnt = cnt_data[MF0_NTAG_AUTHLIM_OFF_IN_CTR] & MF0_NTAG_AUTHLIM_MASK_IN_CTR; uint8_t auth_lim = m_tag_information->memory[first_cfg_page + 1][0] & CONF_ACCESS_AUTHLIM_MASK; if ((auth_lim > 0) && (auth_lim <= auth_cnt)) { nfc_tag_14a_tx_nbit(NAK_INVALID_OPERATION_TBIV, 4); @@ -827,13 +819,13 @@ static void handle_pwd_auth_command(uint8_t *p_data) { uint32_t pwd = *(uint32_t *)m_tag_information->memory[first_cfg_page + CONF_PWD_PAGE_OFFSET]; uint32_t supplied_pwd = *(uint32_t *)&p_data[1]; if (pwd != supplied_pwd) { - if (auth_lim) cnt_data[AUTHLIM_OFF_IN_CTR] |= (auth_cnt + 1) & AUTHLIM_MASK_IN_CTR; + if (auth_lim) cnt_data[MF0_NTAG_AUTHLIM_OFF_IN_CTR] |= (auth_cnt + 1) & MF0_NTAG_AUTHLIM_MASK_IN_CTR; nfc_tag_14a_tx_nbit(NAK_INVALID_OPERATION_TBIV, 4); return; } // reset authentication attempts counter and authenticate user - cnt_data[AUTHLIM_OFF_IN_CTR] = 0; + cnt_data[MF0_NTAG_AUTHLIM_OFF_IN_CTR] = 0; m_tag_authenticated = true; // TODO: this should be possible to reset somehow // Send the PACK value back @@ -844,10 +836,10 @@ static void handle_check_tearing_event(int index) { switch (m_tag_type) { case TAG_TYPE_MF0UL11: case TAG_TYPE_MF0UL21: { - uint8_t *ctr_data = get_counter_data_by_index(index); + uint8_t *ctr_data = nfc_tag_mf0_ntag_get_counter_data_by_index(index); if (ctr_data) { - m_tag_tx_buffer.tx_buffer[0] = (ctr_data[AUTHLIM_OFF_IN_CTR] & TEARING_MASK_IN_AUTHLIM) == 0 ? 0xBD : 0x00; + m_tag_tx_buffer.tx_buffer[0] = (ctr_data[MF0_NTAG_AUTHLIM_OFF_IN_CTR] & MF0_NTAG_TEARING_MASK_IN_AUTHLIM) == 0 ? 0xBD : 0x00; nfc_tag_14a_tx_bytes(m_tag_tx_buffer.tx_buffer, 1, true); } else { nfc_tag_14a_tx_nbit(NAK_INVALID_OPERATION_TBIV, 4); diff --git a/firmware/application/src/rfid/nfctag/hf/nfc_mf0_ntag.h b/firmware/application/src/rfid/nfctag/hf/nfc_mf0_ntag.h index b8518024..5a833561 100644 --- a/firmware/application/src/rfid/nfctag/hf/nfc_mf0_ntag.h +++ b/firmware/application/src/rfid/nfctag/hf/nfc_mf0_ntag.h @@ -35,6 +35,14 @@ #define NFC_TAG_NTAG_FRAME_SIZE 64 #define NFC_TAG_NTAG_BLOCK_MAX NTAG216_TOTAL_PAGES +// Since all counters are 24-bit and each currently supported tag that supports counters +// has password authentication we store the auth attempts counter in the last bit of the +// first counter. AUTHLIM is only 3 bits though so we reserve 4 bits just to be sure and +// use the top bit for tearing event flag. +#define MF0_NTAG_AUTHLIM_OFF_IN_CTR 3 +#define MF0_NTAG_AUTHLIM_MASK_IN_CTR 0xF +#define MF0_NTAG_TEARING_MASK_IN_AUTHLIM 0x80 + typedef struct { uint8_t mode_uid_magic: 1; // reserve @@ -60,6 +68,7 @@ int nfc_tag_mf0_ntag_data_loadcb(tag_specific_type_t type, tag_data_buffer_t *bu int nfc_tag_mf0_ntag_data_savecb(tag_specific_type_t type, tag_data_buffer_t *buffer); bool nfc_tag_mf0_ntag_data_factory(uint8_t slot, tag_specific_type_t tag_type); int nfc_tag_mf0_ntag_get_nr_pages_by_tag_type(tag_specific_type_t tag_type); +uint8_t *nfc_tag_mf0_ntag_get_counter_data_by_index(uint8_t index); uint8_t *nfc_tag_mf0_ntag_get_version_data(void); uint8_t *nfc_tag_mf0_ntag_get_signature_data(void); diff --git a/software/script/chameleon_cli_unit.py b/software/script/chameleon_cli_unit.py index e6524c07..10980daa 100644 --- a/software/script/chameleon_cli_unit.py +++ b/software/script/chameleon_cli_unit.py @@ -1670,6 +1670,43 @@ def on_exec(self, args: argparse.Namespace): f'- {"Log (mfkey32) mode:":40}{f"{CG}enabled{C0}" if detection else f"{CR}disabled{C0}"}') +@hf_mfu.command('ercnt') +class HFMFUVERSION(ReaderRequiredUnit): + def args_parser(self) -> ArgumentParserNoExit: + parser = ArgumentParserNoExit() + parser.description = 'Read MIFARE Ultralight / NTAG counter value.' + parser.add_argument('-c', '--counter', type=int, required=True, help="Counter index.") + return parser + + def on_exec(self, args: argparse.Namespace): + value, no_tearing = self.cmd.mfu_read_emu_counter_data(args.counter) + print(f" - Value: {value:06x}") + if no_tearing: + print(f" - Tearing: {CG}not set{C0}") + else: + print(f" - Tearing: {CR}set{C0}") + + +@hf_mfu.command('ewcnt') +class HFMFUVERSION(ReaderRequiredUnit): + def args_parser(self) -> ArgumentParserNoExit: + parser = ArgumentParserNoExit() + parser.description = 'Read MIFARE Ultralight / NTAG counter value.' + parser.add_argument('-c', '--counter', type=int, required=True, help="Counter index.") + parser.add_argument('-v', '--value', type=int, required=True, help="Counter value (24-bit).") + parser.add_argument('-t', '--reset-tearing', action='store_true', help="Reset tearing event flag.") + return parser + + def on_exec(self, args: argparse.Namespace): + if args.value > 0xFFFFFF: + print(f"{CR}Counter value {args.value:#x} is too large.{C0}") + return + + self.cmd.mfu_write_emu_counter_data(args.counter, args.value, args.reset_tearing) + + print('- Ok') + + @hf_mfu.command('rdpg') class HFMFURDPG(MFUAuthArgsUnit): def args_parser(self) -> ArgumentParserNoExit: diff --git a/software/script/chameleon_cmd.py b/software/script/chameleon_cmd.py index 7ff952f7..43bbd575 100644 --- a/software/script/chameleon_cmd.py +++ b/software/script/chameleon_cmd.py @@ -608,6 +608,26 @@ def mfu_write_emu_page_data(self, page_start: int, data: bytes): resp = self.device.send_cmd_sync(Command.MF0_NTAG_WRITE_EMU_PAGE_DATA, data) return resp + @expect_response(Status.SUCCESS) + def mfu_read_emu_counter_data(self, index: int) -> (int, bool): + """ + Gets data for selected counter + """ + data = struct.pack('!B', index) + resp = self.device.send_cmd_sync(Command.MF0_NTAG_GET_COUNTER_DATA, data) + if resp.status == Status.SUCCESS: + resp.parsed = (((resp.data[0] << 16) | (resp.data[1] << 8) | resp.data[2]), resp.data[3] == 0xBD) + return resp + + @expect_response(Status.SUCCESS) + def mfu_write_emu_counter_data(self, index: int, value: int, reset_tearing: bool): + """ + Sets data for selected counter + """ + data = struct.pack('!BBBB', index | (int(reset_tearing) << 7), (value >> 16) & 0xFF, (value >> 8) & 0xFF, value & 0xFF) + resp = self.device.send_cmd_sync(Command.MF0_NTAG_SET_COUNTER_DATA, data) + return resp + @expect_response(Status.SUCCESS) def hf14a_set_anti_coll_data(self, uid: bytes, atqa: bytes, sak: bytes, ats: bytes = b''): """ diff --git a/software/script/chameleon_enum.py b/software/script/chameleon_enum.py index c88bd54b..c250afdc 100644 --- a/software/script/chameleon_enum.py +++ b/software/script/chameleon_enum.py @@ -103,6 +103,8 @@ class Command(enum.IntEnum): MF0_NTAG_SET_VERSION_DATA = 4024 MF0_NTAG_GET_SIGNATURE_DATA = 4025 MF0_NTAG_SET_SIGNATURE_DATA = 4026 + MF0_NTAG_GET_COUNTER_DATA = 4027 + MF0_NTAG_SET_COUNTER_DATA = 4028 EM410X_SET_EMU_ID = 5000 EM410X_GET_EMU_ID = 5001