From afed67ca2ccf43ad406c45f7957112d017029a42 Mon Sep 17 00:00:00 2001 From: Antiklesys Date: Thu, 12 Dec 2024 18:54:56 +0800 Subject: [PATCH 01/18] Added hf iclass trbl --- CHANGELOG.md | 1 + client/src/cmdhficlass.c | 195 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 196 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2aed8d508..fa298c52c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ All notable changes to this project will be documented in this file. This project uses the changelog in accordance with [keepchangelog](http://keepachangelog.com/). Please use this to write notable changes, which is not the same as git commit log... ## [unreleased][unreleased] +- Added `hf iclass trbl` to perform tear-off attacks on iClass (@antiklesys) - Changed `uniq.yaml` workflow to be case-insensitive (@iceman1001) - Fixed `mem load --mfc` not erasing all SPI flash blocks after extending to 4095 keys (@piotrva) - Extended area for Mifare keys in SPI flash to hold 4095 keys (@piotrva) diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index 42d23d0f1..18ade8ad4 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -41,6 +41,7 @@ #include "preferences.h" #include "generator.h" #include "cmdhf14b.h" +#include "cmdhw.h" #define NUM_CSNS 9 @@ -2927,6 +2928,199 @@ static int CmdHFiClass_ReadBlock(const char *Cmd) { return PM3_SUCCESS; } +static int CmdHFiClass_TearBlock(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf iclass trbl", + "Tear off an iCLASS tag block", + "hf iclass trbl --blk 10 -d AAAAAAAAAAAAAAAA -k 001122334455667B --tdb 100 --tde 150\n" + "hf iclass trbl --blk 10 -d AAAAAAAAAAAAAAAA --ki 0 --tdb 100 --tde 150"); + + void *argtable[] = { + arg_param_begin, + arg_str0("k", "key", "", "Access key as 8 hex bytes"), + arg_int0(NULL, "ki", "", "Key index to select key from memory 'hf iclass managekeys'"), + arg_int1(NULL, "blk", "", "block number"), + arg_str1("d", "data", "", "data to write as 8 hex bytes"), + arg_str0("m", "mac", "", "replay mac data (4 hex bytes)"), + arg_lit0(NULL, "credit", "key is assumed to be the credit key"), + arg_lit0(NULL, "elite", "elite computations applied to key"), + arg_lit0(NULL, "raw", "no computations applied to key"), + arg_lit0(NULL, "nr", "replay of NR/MAC"), + arg_lit0("v", "verbose", "verbose output"), + arg_lit0(NULL, "shallow", "use shallow (ASK) reader modulation instead of OOK"), + arg_int1(NULL, "tdb", "", "tearoff delay start in ms"), + arg_int1(NULL, "tde", "", "tearoff delay end in ms"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + int key_len = 0; + uint8_t key[8] = {0}; + + CLIGetHexWithReturn(ctx, 1, key, &key_len); + + int key_nr = arg_get_int_def(ctx, 2, -1); + + if (key_len > 0 && key_nr >= 0) { + PrintAndLogEx(ERR, "Please specify key or index, not both"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + + bool auth = false; + + if (key_len > 0) { + auth = true; + if (key_len != 8) { + PrintAndLogEx(ERR, "Key is incorrect length"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + } else if (key_nr >= 0) { + if (key_nr < ICLASS_KEYS_MAX) { + auth = true; + memcpy(key, iClass_Key_Table[key_nr], 8); + PrintAndLogEx(SUCCESS, "Using key[%d] " _GREEN_("%s"), key_nr, sprint_hex(iClass_Key_Table[key_nr], 8)); + } else { + PrintAndLogEx(ERR, "Key number is invalid"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + } + + int blockno = arg_get_int_def(ctx, 3, 0); + + int data_len = 0; + uint8_t data[8] = {0}; + CLIGetHexWithReturn(ctx, 4, data, &data_len); + + if (data_len != 8) { + PrintAndLogEx(ERR, "Data must be 8 hex bytes (16 hex symbols)"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + + int mac_len = 0; + uint8_t mac[4] = {0}; + CLIGetHexWithReturn(ctx, 5, mac, &mac_len); + + if (mac_len) { + if (mac_len != 4) { + PrintAndLogEx(ERR, "MAC must be 4 hex bytes (8 hex symbols)"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + } + + + bool use_credit_key = arg_get_lit(ctx, 6); + bool elite = arg_get_lit(ctx, 7); + bool rawkey = arg_get_lit(ctx, 8); + bool use_replay = arg_get_lit(ctx, 9); + bool verbose = arg_get_lit(ctx, 10); + bool shallow_mod = arg_get_lit(ctx, 11); + int tearoff_start = arg_get_int_def(ctx, 12, 100); + int tearoff_end = arg_get_int_def(ctx, 13, 200); + + if(tearoff_end <= tearoff_start){ + PrintAndLogEx(ERR, "Tearoff end delay must be bigger than the start delay."); + return PM3_EINVARG; + } + + if(tearoff_start < 0 || tearoff_end <= 0){ + PrintAndLogEx(ERR, "Tearoff start/end delays should be bigger than 0."); + return PM3_EINVARG; + } + + CLIParserFree(ctx); + + if ((use_replay + rawkey + elite) > 1) { + PrintAndLogEx(ERR, "Can not use a combo of 'elite', 'raw', 'nr'"); + return PM3_EINVARG; + } + int isok = 0; + tearoff_params_t params; + bool read_ok = false; + while(tearoff_start < tearoff_end && !read_ok){ + //perform read here, repeat if failed or 00s + + uint8_t data_read_orig[8] = {0}; + bool first_read = false; + bool reread = false; + while(!first_read){ + int res_orig = iclass_read_block_ex(key, blockno, 0x88, elite, rawkey, use_replay, verbose, auth, shallow_mod, data_read_orig, false); + if (res_orig == PM3_SUCCESS && !reread){ + if (memcmp(data_read_orig, zeros, 8) == 0){ + reread = true; + }else{ + first_read = true; + reread = false; + } + } else if (res_orig == PM3_SUCCESS && reread){ + first_read = true; + reread = false; + } + } + + params.on = true; + params.delay_us = tearoff_start; + handle_tearoff(¶ms, false); + PrintAndLogEx(INFO, "Tear off delay: "_YELLOW_("%d")" ms", tearoff_start); + isok = iclass_write_block(blockno, data, mac, key, use_credit_key, elite, rawkey, use_replay, verbose, auth, shallow_mod); + switch (isok) { + case PM3_SUCCESS: + PrintAndLogEx(SUCCESS, "Wrote block " _YELLOW_("%d") " / " _YELLOW_("0x%02X") " ( " _GREEN_("ok") " )", blockno, blockno); + break; + case PM3_ETEAROFF: + break; + default: + PrintAndLogEx(FAILED, "Writing failed"); + break; + } + //read the data back + uint8_t data_read[8] = {0}; + first_read = false; + reread = false; + bool decrease = false; + while(!first_read){ + int res = iclass_read_block_ex(key, blockno, 0x88, elite, rawkey, use_replay, verbose, auth, shallow_mod, data_read, false); + if (res == PM3_SUCCESS && !reread){ + if (memcmp(data_read, zeros, 8) == 0){ + reread = true; + }else{ + first_read = true; + reread = false; + } + } else if (res == PM3_SUCCESS && reread){ + first_read = true; + reread = false; + } else if (res != PM3_SUCCESS){ + decrease = true; + } + } + if (decrease && tearoff_start > 0){ //if there was an error reading repeat the tearoff with the same delay + tearoff_start--; + } + bool tear_success = true; + for (int i=0; i Date: Fri, 13 Dec 2024 16:03:45 +0800 Subject: [PATCH 02/18] Update cmdhficlass.c Signed-off-by: Antiklesys --- client/src/cmdhficlass.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index 18ade8ad4..fbe8a88bc 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -3012,13 +3012,6 @@ static int CmdHFiClass_TearBlock(const char *Cmd) { } } - - bool use_credit_key = arg_get_lit(ctx, 6); - bool elite = arg_get_lit(ctx, 7); - bool rawkey = arg_get_lit(ctx, 8); - bool use_replay = arg_get_lit(ctx, 9); - bool verbose = arg_get_lit(ctx, 10); - bool shallow_mod = arg_get_lit(ctx, 11); int tearoff_start = arg_get_int_def(ctx, 12, 100); int tearoff_end = arg_get_int_def(ctx, 13, 200); @@ -3032,6 +3025,13 @@ static int CmdHFiClass_TearBlock(const char *Cmd) { return PM3_EINVARG; } + bool use_credit_key = arg_get_lit(ctx, 6); + bool elite = arg_get_lit(ctx, 7); + bool rawkey = arg_get_lit(ctx, 8); + bool use_replay = arg_get_lit(ctx, 9); + bool verbose = arg_get_lit(ctx, 10); + bool shallow_mod = arg_get_lit(ctx, 11); + CLIParserFree(ctx); if ((use_replay + rawkey + elite) > 1) { From d7ab949d1f1dfc5f99c1047af334b50daca9664e Mon Sep 17 00:00:00 2001 From: Piotr Rzeszut Date: Wed, 25 Dec 2024 19:14:05 +0100 Subject: [PATCH 03/18] Change hf mf fchk --mem to read dictionary from spiffs file --- armsrc/mifarecmd.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/armsrc/mifarecmd.c b/armsrc/mifarecmd.c index 9820d19a2..928b64cb4 100644 --- a/armsrc/mifarecmd.c +++ b/armsrc/mifarecmd.c @@ -38,6 +38,8 @@ #include "spiffs.h" // spiffs #include "appmain.h" // print_stack_usage +#define MF_KEYS_FILE "dict_mf.bin" + #ifndef HARDNESTED_AUTHENTICATION_TIMEOUT # define HARDNESTED_AUTHENTICATION_TIMEOUT 848 // card times out 1ms after wrong authentication (according to NXP documentation) #endif @@ -1900,13 +1902,12 @@ void MifareChkKeys_fast(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *da #ifdef WITH_FLASH if (use_flashmem) { BigBuf_free(); - uint16_t isok = 0; - uint8_t size[2] = {0x00, 0x00}; - isok = Flash_ReadData(DEFAULT_MF_KEYS_OFFSET_P(spi_flash_pages64k), size, 2); - if (isok != 2) + uint32_t size = 0; + size = size_in_spiffs(MF_KEYS_FILE); + if (size <= 0) goto OUT; - keyCount = size[1] << 8 | size[0]; + keyCount = size / 6; if (keyCount == 0) goto OUT; @@ -1921,10 +1922,8 @@ void MifareChkKeys_fast(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *da if (datain == NULL) goto OUT; - isok = Flash_ReadData(DEFAULT_MF_KEYS_OFFSET_P(spi_flash_pages64k) + 2, datain, key_mem_available); - if (isok != key_mem_available) - goto OUT; - + rdv40_spiffs_read_as_filetype(MF_KEYS_FILE, datain, keyCount * 6, RDV40_SPIFFS_SAFETY_SAFE); + if (g_dbglevel >= DBG_ERROR) Dbprintf("Loaded %u keys from spiffs file: %s", keyCount, MF_KEYS_FILE); } #endif From 5af815f2713c2f6572db65f6bcd41ca1f509f6e8 Mon Sep 17 00:00:00 2001 From: Piotr Rzeszut Date: Wed, 25 Dec 2024 21:08:44 +0100 Subject: [PATCH 04/18] Mifare dictionary uploaded to spiffs using legacy function --- armsrc/appmain.c | 19 -------- armsrc/mifarecmd.c | 2 - client/src/cmdflashmem.c | 94 ++++++++++++++++++++++------------------ include/pmflash.h | 2 + 4 files changed, 53 insertions(+), 64 deletions(-) diff --git a/armsrc/appmain.c b/armsrc/appmain.c index 2422db883..fbadeafad 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -2752,25 +2752,6 @@ static void PacketReceived(PacketCommandNG *packet) { Flash_CheckBusy(BUSY_TIMEOUT); Flash_WriteEnable(); Flash_Erase4k(spi_flash_pages64k - 1, 0xC); - } else if (payload->startidx == DEFAULT_MF_KEYS_OFFSET_P(spi_flash_pages64k)) { - Flash_CheckBusy(BUSY_TIMEOUT); - Flash_WriteEnable(); - Flash_Erase4k(spi_flash_pages64k - 1, 0x5); - Flash_CheckBusy(BUSY_TIMEOUT); - Flash_WriteEnable(); - Flash_Erase4k(spi_flash_pages64k - 1, 0x6); - Flash_CheckBusy(BUSY_TIMEOUT); - Flash_WriteEnable(); - Flash_Erase4k(spi_flash_pages64k - 1, 0x7); - Flash_CheckBusy(BUSY_TIMEOUT); - Flash_WriteEnable(); - Flash_Erase4k(spi_flash_pages64k - 1, 0x8); - Flash_CheckBusy(BUSY_TIMEOUT); - Flash_WriteEnable(); - Flash_Erase4k(spi_flash_pages64k - 1, 0x9); - Flash_CheckBusy(BUSY_TIMEOUT); - Flash_WriteEnable(); - Flash_Erase4k(spi_flash_pages64k - 1, 0xA); } else if (payload->startidx == DEFAULT_ICLASS_KEYS_OFFSET_P(spi_flash_pages64k)) { Flash_CheckBusy(BUSY_TIMEOUT); Flash_WriteEnable(); diff --git a/armsrc/mifarecmd.c b/armsrc/mifarecmd.c index 928b64cb4..170d209e6 100644 --- a/armsrc/mifarecmd.c +++ b/armsrc/mifarecmd.c @@ -38,8 +38,6 @@ #include "spiffs.h" // spiffs #include "appmain.h" // print_stack_usage -#define MF_KEYS_FILE "dict_mf.bin" - #ifndef HARDNESTED_AUTHENTICATION_TIMEOUT # define HARDNESTED_AUTHENTICATION_TIMEOUT 848 // card times out 1ms after wrong authentication (according to NXP documentation) #endif diff --git a/client/src/cmdflashmem.c b/client/src/cmdflashmem.c index d1e9ab672..23a43fe57 100644 --- a/client/src/cmdflashmem.c +++ b/client/src/cmdflashmem.c @@ -192,7 +192,7 @@ static int CmdFlashMemLoad(const char *Cmd) { CLIParserInit(&ctx, "mem load", "Loads binary file into flash memory on device\n" "Warning: mem area to be written must have been wiped first\n" - "( this is already taken care when loading dictionaries )", + "( dictionaries are serviced as files in spiffs so no wipe is needed )", "mem load -f myfile -> upload file myfile values at default offset 0\n" "mem load -f myfile -o 1024 -> upload file myfile values at offset 1024\n" "mem load -f mfc_default_keys -m -> upload MFC keys\n" @@ -217,6 +217,7 @@ static int CmdFlashMemLoad(const char *Cmd) { bool is_t55xx = arg_get_lit(ctx, 4); int fnlen = 0; char filename[FILE_PATH_SIZE] = {0}; + char spiffsDest[32] = {0}; CLIParamStrToBuf(arg_get_str(ctx, 5), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); CLIParserFree(ctx); @@ -246,22 +247,18 @@ static int CmdFlashMemLoad(const char *Cmd) { switch (d) { case DICTIONARY_MIFARE: - offset = DEFAULT_MF_KEYS_OFFSET_P(spi_flash_pages); keylen = 6; - res = loadFileDICTIONARY(filename, data + 2, &datalen, keylen, &keycount); + res = loadFileDICTIONARY(filename, data, &datalen, keylen, &keycount); if (res || !keycount) { free(data); return PM3_EFILE; } - // limited space on flash mem - if (keycount > DEFAULT_MF_KEYS_MAX) { - keycount = DEFAULT_MF_KEYS_MAX; - datalen = keycount * keylen; + if (datalen > FLASH_MEM_MAX_SIZE_P(spi_flash_pages)) { + PrintAndLogEx(ERR, "error, filesize is larger than available memory"); + free(data); + return PM3_EOVFLOW; } - - data[0] = (keycount >> 0) & 0xFF; - data[1] = (keycount >> 8) & 0xFF; - datalen += 2; + strcpy_s(spiffsDest, 32, MF_KEYS_FILE); break; case DICTIONARY_T55XX: offset = DEFAULT_T55XX_KEYS_OFFSET_P(spi_flash_pages); @@ -326,44 +323,55 @@ static int CmdFlashMemLoad(const char *Cmd) { uint32_t bytes_sent = 0; uint32_t bytes_remaining = datalen; - - // fast push mode - g_conn.block_after_ACK = true; - - while (bytes_remaining > 0) { - uint32_t bytes_in_packet = MIN(FLASH_MEM_BLOCK_SIZE, bytes_remaining); - - clearCommandBuffer(); - - flashmem_old_write_t payload = { - .startidx = offset + bytes_sent, - .len = bytes_in_packet, - }; - memcpy(payload.data, data + bytes_sent, bytes_in_packet); - SendCommandNG(CMD_FLASHMEM_WRITE, (uint8_t *)&payload, sizeof(payload)); - - bytes_remaining -= bytes_in_packet; - bytes_sent += bytes_in_packet; - - PacketResponseNG resp; - if (WaitForResponseTimeout(CMD_FLASHMEM_WRITE, &resp, 2000) == false) { - PrintAndLogEx(WARNING, "timeout while waiting for reply."); - g_conn.block_after_ACK = false; + // we will treat dictionary files as spiffs files, so we need to handle this here + if (d == DICTIONARY_MIFARE) { + res = flashmem_spiffs_load(spiffsDest, data, datalen); + if (res != PM3_SUCCESS) { + PrintAndLogEx(FAILED, "Failed writing passwrods to file %s", spiffsDest); free(data); - return PM3_ETIMEOUT; + return res; + } + PrintAndLogEx(SUCCESS, "Wrote "_GREEN_("%u")" passwords to file "_GREEN_("%s"), keycount, spiffsDest); + } else { + // fast push mode + g_conn.block_after_ACK = true; + + while (bytes_remaining > 0) { + uint32_t bytes_in_packet = MIN(FLASH_MEM_BLOCK_SIZE, bytes_remaining); + + clearCommandBuffer(); + + flashmem_old_write_t payload = { + .startidx = offset + bytes_sent, + .len = bytes_in_packet, + }; + memcpy(payload.data, data + bytes_sent, bytes_in_packet); + SendCommandNG(CMD_FLASHMEM_WRITE, (uint8_t *)&payload, sizeof(payload)); + + bytes_remaining -= bytes_in_packet; + bytes_sent += bytes_in_packet; + + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_FLASHMEM_WRITE, &resp, 2000) == false) { + PrintAndLogEx(WARNING, "timeout while waiting for reply."); + g_conn.block_after_ACK = false; + free(data); + return PM3_ETIMEOUT; + } + + if (resp.status != PM3_SUCCESS) { + g_conn.block_after_ACK = false; + PrintAndLogEx(FAILED, "Flash write fail [offset %u]", bytes_sent); + free(data); + return PM3_EFLASH; + } } - if (resp.status != PM3_SUCCESS) { - g_conn.block_after_ACK = false; - PrintAndLogEx(FAILED, "Flash write fail [offset %u]", bytes_sent); - free(data); - return PM3_EFLASH; - } + g_conn.block_after_ACK = false; + PrintAndLogEx(SUCCESS, "Wrote "_GREEN_("%zu")" bytes to offset "_GREEN_("%u"), datalen, offset); } - g_conn.block_after_ACK = false; free(data); - PrintAndLogEx(SUCCESS, "Wrote "_GREEN_("%zu")" bytes to offset "_GREEN_("%u"), datalen, offset); return PM3_SUCCESS; } diff --git a/include/pmflash.h b/include/pmflash.h index 8f9f9c741..1e2cdcc52 100644 --- a/include/pmflash.h +++ b/include/pmflash.h @@ -96,6 +96,8 @@ #endif // Reserved space for MIFARE Keys = 24 kb +#define MF_KEYS_FILE "dict_mf.bin" + #ifndef DEFAULT_MF_KEYS_OFFSET # define DEFAULT_MF_KEYS_LEN (0x6000) # define DEFAULT_MF_KEYS_OFFSET (DEFAULT_ICLASS_KEYS_OFFSET - DEFAULT_MF_KEYS_LEN) From 9242d2f956d120152595dc8e605bef62fcb2850d Mon Sep 17 00:00:00 2001 From: Piotr Rzeszut Date: Wed, 25 Dec 2024 23:51:54 +0100 Subject: [PATCH 05/18] Increase timeout to about 1400s in hf mf fchk to be able to run much more than 2100 keys check --- client/src/mifare/mifarehost.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/mifare/mifarehost.c b/client/src/mifare/mifarehost.c index 7048173b4..e9a8232e8 100644 --- a/client/src/mifare/mifarehost.c +++ b/client/src/mifare/mifarehost.c @@ -273,7 +273,7 @@ int mf_check_keys_fast_ex(uint8_t sectorsCnt, uint8_t firstChunk, uint8_t lastCh // max timeout for one chunk of 85keys, 60*3sec = 180seconds // s70 with 40*2 keys to check, 80*85 = 6800 auth. // takes about 97s, still some margin before abort - if (timeout > 180) { + if (timeout > 60*6) { PrintAndLogEx(WARNING, "\nNo response from Proxmark3. Aborting..."); return PM3_ETIMEOUT; } From 3b9ba0ffe5eb1310c4abc81f28f98a874af29741 Mon Sep 17 00:00:00 2001 From: Piotr Rzeszut Date: Thu, 26 Dec 2024 00:16:21 +0100 Subject: [PATCH 06/18] Increase timeout to about 1400s in hf mf fchk to be able to run much more than 2100 keys check --- client/src/mifare/mifarehost.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/client/src/mifare/mifarehost.c b/client/src/mifare/mifarehost.c index e9a8232e8..453a3b447 100644 --- a/client/src/mifare/mifarehost.c +++ b/client/src/mifare/mifarehost.c @@ -273,7 +273,9 @@ int mf_check_keys_fast_ex(uint8_t sectorsCnt, uint8_t firstChunk, uint8_t lastCh // max timeout for one chunk of 85keys, 60*3sec = 180seconds // s70 with 40*2 keys to check, 80*85 = 6800 auth. // takes about 97s, still some margin before abort - if (timeout > 60*6) { + // timeout = 180 => ~360s @ Mifare Classic 1k @ ~2300 keys in dict + // ~2300 keys @ Mifare Classic 1k => ~620s + if (timeout > 60*12) { PrintAndLogEx(WARNING, "\nNo response from Proxmark3. Aborting..."); return PM3_ETIMEOUT; } From d9a3e4f05074455590ae78b6066aee5c233c4a35 Mon Sep 17 00:00:00 2001 From: Piotr Rzeszut Date: Thu, 26 Dec 2024 01:11:46 +0100 Subject: [PATCH 07/18] Refactor keys in flash statistics to check for files --- armsrc/appmain.c | 23 ++++++++++++++++++++++- armsrc/mifarecmd.c | 8 ++++---- client/src/cmdflashmem.c | 2 +- common_arm/flashmem.c | 37 ------------------------------------- common_arm/flashmem.h | 1 - include/pmflash.h | 7 +++++++ 6 files changed, 34 insertions(+), 44 deletions(-) diff --git a/armsrc/appmain.c b/armsrc/appmain.c index fbadeafad..bec1de61a 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -440,7 +440,28 @@ static void SendStatus(uint32_t wait) { ModInfo(); #ifdef WITH_FLASH - Flashmem_print_info(); + DbpString(_CYAN_("Flash memory dictionary loaded")); + + uint32_t num = size_in_spiffs(MF_KEYS_FILE) / MF_KEY_LENGTH; + if (num > 0) { + Dbprintf(" Mifare.................. "_YELLOW_("%u")" keys (spiffs: "_GREEN_("%s")")", num, MF_KEYS_FILE); + } else { + Dbprintf(" Mifare.................. "_RED_("%u")" keys (spiffs: "_RED_("%s")")", num, MF_KEYS_FILE); + } + + num = size_in_spiffs(T55XX_KEYS_FILE) / T55XX_KEY_LENGTH; + if (num > 0) { + Dbprintf(" T55xx................... "_YELLOW_("%u")" keys (spiffs: "_GREEN_("%s")")", num, T55XX_KEYS_FILE); + } else { + Dbprintf(" T55xx................... "_RED_("%u")" keys (spiffs: "_RED_("%s")")", num, T55XX_KEYS_FILE); + } + + num = size_in_spiffs(ICLASS_KEYS_FILE) / ICLASS_KEY_LENGTH; + if (num > 0) { + Dbprintf(" iClass.................. "_YELLOW_("%u")" keys (spiffs: "_GREEN_("%s")")", num, ICLASS_KEYS_FILE); + } else { + Dbprintf(" iClass.................. "_RED_("%u")" keys (spiffs: "_RED_("%s")")", num, ICLASS_KEYS_FILE); + } #endif DbpString(""); reply_ng(CMD_STATUS, PM3_SUCCESS, NULL, 0); diff --git a/armsrc/mifarecmd.c b/armsrc/mifarecmd.c index 170d209e6..984937c4f 100644 --- a/armsrc/mifarecmd.c +++ b/armsrc/mifarecmd.c @@ -1905,22 +1905,22 @@ void MifareChkKeys_fast(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *da if (size <= 0) goto OUT; - keyCount = size / 6; + keyCount = size / MF_KEY_LENGTH; if (keyCount == 0) goto OUT; // limit size of available for keys in bigbuff // a key is 6bytes - uint16_t key_mem_available = MIN(BigBuf_get_size(), keyCount * 6); + uint16_t key_mem_available = MIN(BigBuf_get_size(), keyCount * MF_KEY_LENGTH); - keyCount = key_mem_available / 6; + keyCount = key_mem_available / MF_KEY_LENGTH; datain = BigBuf_malloc(key_mem_available); if (datain == NULL) goto OUT; - rdv40_spiffs_read_as_filetype(MF_KEYS_FILE, datain, keyCount * 6, RDV40_SPIFFS_SAFETY_SAFE); + rdv40_spiffs_read_as_filetype(MF_KEYS_FILE, datain, keyCount * MF_KEY_LENGTH, RDV40_SPIFFS_SAFETY_SAFE); if (g_dbglevel >= DBG_ERROR) Dbprintf("Loaded %u keys from spiffs file: %s", keyCount, MF_KEYS_FILE); } #endif diff --git a/client/src/cmdflashmem.c b/client/src/cmdflashmem.c index 23a43fe57..ea5a7436d 100644 --- a/client/src/cmdflashmem.c +++ b/client/src/cmdflashmem.c @@ -247,7 +247,7 @@ static int CmdFlashMemLoad(const char *Cmd) { switch (d) { case DICTIONARY_MIFARE: - keylen = 6; + keylen = MF_KEY_LENGTH; res = loadFileDICTIONARY(filename, data, &datalen, keylen, &keycount); if (res || !keycount) { free(data); diff --git a/common_arm/flashmem.c b/common_arm/flashmem.c index 76b475e9c..f515fe994 100644 --- a/common_arm/flashmem.c +++ b/common_arm/flashmem.c @@ -383,43 +383,6 @@ void Flashmem_print_status(void) { FlashStop(); } -void Flashmem_print_info(void) { - - if (!FlashInit()) return; - - DbpString(_CYAN_("Flash memory dictionary loaded")); - - // load dictionary offsets. - uint8_t keysum[2]; - uint16_t num; - - Flash_CheckBusy(BUSY_TIMEOUT); - uint16_t isok = Flash_ReadDataCont(DEFAULT_MF_KEYS_OFFSET_P(spi_flash_pages64k), keysum, 2); - if (isok == 2) { - num = ((keysum[1] << 8) | keysum[0]); - if (num != 0xFFFF && num != 0x0) - Dbprintf(" Mifare.................. "_YELLOW_("%u")" / "_GREEN_("%u")" keys", num, DEFAULT_MF_KEYS_MAX); - } - - Flash_CheckBusy(BUSY_TIMEOUT); - isok = Flash_ReadDataCont(DEFAULT_T55XX_KEYS_OFFSET_P(spi_flash_pages64k), keysum, 2); - if (isok == 2) { - num = ((keysum[1] << 8) | keysum[0]); - if (num != 0xFFFF && num != 0x0) - Dbprintf(" T55x7................... "_YELLOW_("%u")" / "_GREEN_("%u")" keys", num, DEFAULT_T55XX_KEYS_MAX); - } - - Flash_CheckBusy(BUSY_TIMEOUT); - isok = Flash_ReadDataCont(DEFAULT_ICLASS_KEYS_OFFSET_P(spi_flash_pages64k), keysum, 2); - if (isok == 2) { - num = ((keysum[1] << 8) | keysum[0]); - if (num != 0xFFFF && num != 0x0) - Dbprintf(" iClass.................. "_YELLOW_("%u")" / "_GREEN_("%u")" keys", num, DEFAULT_ICLASS_KEYS_MAX); - } - - FlashStop(); -} - bool FlashDetect(void) { flash_device_type_t flash_data = {0}; bool ret = false; diff --git a/common_arm/flashmem.h b/common_arm/flashmem.h index f20dd0bff..45b8e67c2 100644 --- a/common_arm/flashmem.h +++ b/common_arm/flashmem.h @@ -134,7 +134,6 @@ uint16_t Flash_Write(uint32_t address, uint8_t *in, uint16_t len); uint16_t Flash_WriteData(uint32_t address, uint8_t *in, uint16_t len); uint16_t Flash_WriteDataCont(uint32_t address, uint8_t *in, uint16_t len); void Flashmem_print_status(void); -void Flashmem_print_info(void); typedef struct { uint8_t manufacturer_id; diff --git a/include/pmflash.h b/include/pmflash.h index 1e2cdcc52..027e2fa80 100644 --- a/include/pmflash.h +++ b/include/pmflash.h @@ -76,6 +76,9 @@ #endif // Reserved space for T55XX PWD = 4 kb +#define T55XX_KEYS_FILE "dict_t55xx.bin" +#define T55XX_KEY_LENGTH 4 + #ifndef DEFAULT_T55XX_KEYS_OFFSET # define DEFAULT_T55XX_KEYS_LEN (0x1000) # define DEFAULT_T55XX_KEYS_OFFSET (T55XX_CONFIG_OFFSET - DEFAULT_T55XX_KEYS_LEN) @@ -86,6 +89,9 @@ #endif // Reserved space for iClass keys = 4 kb +#define ICLASS_KEYS_FILE "dict_iclass.bin" +#define ICLASS_KEY_LENGTH 8 + #ifndef DEFAULT_ICLASS_KEYS_OFFSET # define DEFAULT_ICLASS_KEYS_LEN (0x1000) # define DEFAULT_ICLASS_KEYS_OFFSET (DEFAULT_T55XX_KEYS_OFFSET - DEFAULT_ICLASS_KEYS_LEN) @@ -97,6 +103,7 @@ // Reserved space for MIFARE Keys = 24 kb #define MF_KEYS_FILE "dict_mf.bin" +#define MF_KEY_LENGTH 6 #ifndef DEFAULT_MF_KEYS_OFFSET # define DEFAULT_MF_KEYS_LEN (0x6000) From dd646a64a688a403dc15f0710cb7cdfdb601f28e Mon Sep 17 00:00:00 2001 From: Piotr Rzeszut Date: Thu, 26 Dec 2024 01:25:54 +0100 Subject: [PATCH 08/18] Add additional file exists check to remove errors --- armsrc/appmain.c | 19 ++++++++++++++++--- armsrc/mifarecmd.c | 8 ++++++-- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/armsrc/appmain.c b/armsrc/appmain.c index bec1de61a..b9314a0a3 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -441,22 +441,35 @@ static void SendStatus(uint32_t wait) { #ifdef WITH_FLASH DbpString(_CYAN_("Flash memory dictionary loaded")); + uint32_t num = 0; - uint32_t num = size_in_spiffs(MF_KEYS_FILE) / MF_KEY_LENGTH; + if (exists_in_spiffs(MF_KEYS_FILE)) { + num = size_in_spiffs(MF_KEYS_FILE) / MF_KEY_LENGTH; + } else { + num = 0; + } if (num > 0) { Dbprintf(" Mifare.................. "_YELLOW_("%u")" keys (spiffs: "_GREEN_("%s")")", num, MF_KEYS_FILE); } else { Dbprintf(" Mifare.................. "_RED_("%u")" keys (spiffs: "_RED_("%s")")", num, MF_KEYS_FILE); } - num = size_in_spiffs(T55XX_KEYS_FILE) / T55XX_KEY_LENGTH; + if (exists_in_spiffs(T55XX_KEYS_FILE)) { + num = size_in_spiffs(T55XX_KEYS_FILE) / T55XX_KEY_LENGTH; + } else { + num = 0; + } if (num > 0) { Dbprintf(" T55xx................... "_YELLOW_("%u")" keys (spiffs: "_GREEN_("%s")")", num, T55XX_KEYS_FILE); } else { Dbprintf(" T55xx................... "_RED_("%u")" keys (spiffs: "_RED_("%s")")", num, T55XX_KEYS_FILE); } - num = size_in_spiffs(ICLASS_KEYS_FILE) / ICLASS_KEY_LENGTH; + if (exists_in_spiffs(ICLASS_KEYS_FILE)) { + num = size_in_spiffs(ICLASS_KEYS_FILE) / ICLASS_KEY_LENGTH; + } else { + num = 0; + } if (num > 0) { Dbprintf(" iClass.................. "_YELLOW_("%u")" keys (spiffs: "_GREEN_("%s")")", num, ICLASS_KEYS_FILE); } else { diff --git a/armsrc/mifarecmd.c b/armsrc/mifarecmd.c index 984937c4f..271d87cc4 100644 --- a/armsrc/mifarecmd.c +++ b/armsrc/mifarecmd.c @@ -1901,9 +1901,13 @@ void MifareChkKeys_fast(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *da if (use_flashmem) { BigBuf_free(); uint32_t size = 0; - size = size_in_spiffs(MF_KEYS_FILE); - if (size <= 0) + if (exists_in_spiffs(MF_KEYS_FILE)) { + size = size_in_spiffs(MF_KEYS_FILE); + } + if (size == 0) { + Dbprintf("Spiffs file: %s does not exists or empty.", MF_KEYS_FILE); goto OUT; + } keyCount = size / MF_KEY_LENGTH; From 470536f0fdaf59c6f89c50dbc868c82b9a49e316 Mon Sep 17 00:00:00 2001 From: Piotr Rzeszut Date: Thu, 26 Dec 2024 02:10:49 +0100 Subject: [PATCH 09/18] Upload also iClass and T55XX keys to spiffs files --- client/src/cmdflashmem.c | 39 +++++++++++++++++---------------------- 1 file changed, 17 insertions(+), 22 deletions(-) diff --git a/client/src/cmdflashmem.c b/client/src/cmdflashmem.c index ea5a7436d..3489813ef 100644 --- a/client/src/cmdflashmem.c +++ b/client/src/cmdflashmem.c @@ -261,39 +261,32 @@ static int CmdFlashMemLoad(const char *Cmd) { strcpy_s(spiffsDest, 32, MF_KEYS_FILE); break; case DICTIONARY_T55XX: - offset = DEFAULT_T55XX_KEYS_OFFSET_P(spi_flash_pages); - keylen = 4; - res = loadFileDICTIONARY(filename, data + 2, &datalen, keylen, &keycount); + keylen = T55XX_KEY_LENGTH; + res = loadFileDICTIONARY(filename, data, &datalen, keylen, &keycount); if (res || !keycount) { free(data); return PM3_EFILE; } - // limited space on flash mem - if (keycount > DEFAULT_T55XX_KEYS_MAX) { - keycount = DEFAULT_T55XX_KEYS_MAX; - datalen = keycount * keylen; + if (datalen > FLASH_MEM_MAX_SIZE_P(spi_flash_pages)) { + PrintAndLogEx(ERR, "error, filesize is larger than available memory"); + free(data); + return PM3_EOVFLOW; } - - data[0] = (keycount >> 0) & 0xFF; - data[1] = (keycount >> 8) & 0xFF; - datalen += 2; + strcpy_s(spiffsDest, 32, T55XX_KEYS_FILE); break; case DICTIONARY_ICLASS: - offset = DEFAULT_ICLASS_KEYS_OFFSET_P(spi_flash_pages); - res = loadFileDICTIONARY(filename, data + 2, &datalen, keylen, &keycount); + keylen = ICLASS_KEY_LENGTH; + res = loadFileDICTIONARY(filename, data, &datalen, keylen, &keycount); if (res || !keycount) { free(data); return PM3_EFILE; } - // limited space on flash mem - if (keycount > DEFAULT_ICLASS_KEYS_MAX) { - keycount = DEFAULT_ICLASS_KEYS_MAX; - datalen = keycount * keylen; + if (datalen > FLASH_MEM_MAX_SIZE_P(spi_flash_pages)) { + PrintAndLogEx(ERR, "error, filesize is larger than available memory"); + free(data); + return PM3_EOVFLOW; } - - data[0] = (keycount >> 0) & 0xFF; - data[1] = (keycount >> 8) & 0xFF; - datalen += 2; + strcpy_s(spiffsDest, 32, ICLASS_KEYS_FILE); break; case DICTIONARY_NONE: res = loadFile_safe(filename, ".bin", (void **)&data, &datalen); @@ -324,7 +317,7 @@ static int CmdFlashMemLoad(const char *Cmd) { uint32_t bytes_remaining = datalen; // we will treat dictionary files as spiffs files, so we need to handle this here - if (d == DICTIONARY_MIFARE) { + if (d != DICTIONARY_NONE) { res = flashmem_spiffs_load(spiffsDest, data, datalen); if (res != PM3_SUCCESS) { PrintAndLogEx(FAILED, "Failed writing passwrods to file %s", spiffsDest); @@ -332,6 +325,8 @@ static int CmdFlashMemLoad(const char *Cmd) { return res; } PrintAndLogEx(SUCCESS, "Wrote "_GREEN_("%u")" passwords to file "_GREEN_("%s"), keycount, spiffsDest); + SendCommandNG(CMD_SPIFFS_UNMOUNT, NULL, 0); + SendCommandNG(CMD_SPIFFS_MOUNT, NULL, 0); } else { // fast push mode g_conn.block_after_ACK = true; From 2fe0ba57b2ac80950cdd21a3c241ee89094ede53 Mon Sep 17 00:00:00 2001 From: Piotr Rzeszut Date: Thu, 26 Dec 2024 02:52:59 +0100 Subject: [PATCH 10/18] Change lf t55xx chk -m to read dictionary from spiffs file --- armsrc/appmain.c | 10 +--------- armsrc/lfops.c | 27 +++++++++++++++------------ armsrc/mifarecmd.c | 1 + 3 files changed, 17 insertions(+), 21 deletions(-) diff --git a/armsrc/appmain.c b/armsrc/appmain.c index b9314a0a3..6d42a0e0c 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -2782,15 +2782,7 @@ static void PacketReceived(PacketCommandNG *packet) { break; } - if (payload->startidx == DEFAULT_T55XX_KEYS_OFFSET_P(spi_flash_pages64k)) { - Flash_CheckBusy(BUSY_TIMEOUT); - Flash_WriteEnable(); - Flash_Erase4k(spi_flash_pages64k - 1, 0xC); - } else if (payload->startidx == DEFAULT_ICLASS_KEYS_OFFSET_P(spi_flash_pages64k)) { - Flash_CheckBusy(BUSY_TIMEOUT); - Flash_WriteEnable(); - Flash_Erase4k(spi_flash_pages64k - 1, 0xB); - } else if (payload->startidx == FLASH_MEM_SIGNATURE_OFFSET_P(spi_flash_pages64k)) { + if (payload->startidx == FLASH_MEM_SIGNATURE_OFFSET_P(spi_flash_pages64k)) { Flash_CheckBusy(BUSY_TIMEOUT); Flash_WriteEnable(); Flash_Erase4k(spi_flash_pages64k - 1, 0xF); diff --git a/armsrc/lfops.c b/armsrc/lfops.c index 263905466..a0d3ff09c 100644 --- a/armsrc/lfops.c +++ b/armsrc/lfops.c @@ -37,7 +37,8 @@ #include "protocols.h" #include "pmflash.h" #include "flashmem.h" // persistence on flash -#include "appmain.h" // print stack +#include "spiffs.h" // spiffs +#include "appmain.h" // print stack /* Notes about EM4xxx timings. @@ -2146,29 +2147,31 @@ void T55xx_ChkPwds(uint8_t flags, bool ledcontrol) { #ifdef WITH_FLASH BigBuf_Clear_EM(); - uint16_t isok = 0; - uint8_t counter[2] = {0x00, 0x00}; - isok = Flash_ReadData(DEFAULT_T55XX_KEYS_OFFSET_P(spi_flash_pages64k), counter, sizeof(counter)); - if (isok != sizeof(counter)) + uint32_t size = 0; + + if (exists_in_spiffs(T55XX_KEYS_FILE)) { + size = size_in_spiffs(T55XX_KEYS_FILE); + } + if (size == 0) { + Dbprintf("Spiffs file: %s does not exists or empty.", T55XX_KEYS_FILE); goto OUT; + } - pwd_count = (uint16_t)(counter[1] << 8 | counter[0]); + pwd_count = size / T55XX_KEY_LENGTH; if (pwd_count == 0) goto OUT; // since flash can report way too many pwds, we need to limit it. // bigbuff EM size is determined by CARD_MEMORY_SIZE // a password is 4bytes. - uint16_t pwd_size_available = MIN(CARD_MEMORY_SIZE, pwd_count * 4); + uint16_t pwd_size_available = MIN(CARD_MEMORY_SIZE, pwd_count * T55XX_KEY_LENGTH); // adjust available pwd_count - pwd_count = pwd_size_available / 4; + pwd_count = pwd_size_available / T55XX_KEY_LENGTH; - isok = Flash_ReadData(DEFAULT_T55XX_KEYS_OFFSET_P(spi_flash_pages64k) + 2, pwds, pwd_size_available); - if (isok != pwd_size_available) - goto OUT; + rdv40_spiffs_read_as_filetype(T55XX_KEYS_FILE, pwds, pwd_size_available, RDV40_SPIFFS_SAFETY_SAFE); - Dbprintf("Password dictionary count " _YELLOW_("%d"), pwd_count); + if (g_dbglevel >= DBG_ERROR) Dbprintf("Loaded %u passwords from spiffs file: %s", pwd_count, T55XX_KEYS_FILE); #endif diff --git a/armsrc/mifarecmd.c b/armsrc/mifarecmd.c index 271d87cc4..b59b17a79 100644 --- a/armsrc/mifarecmd.c +++ b/armsrc/mifarecmd.c @@ -1925,6 +1925,7 @@ void MifareChkKeys_fast(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *da goto OUT; rdv40_spiffs_read_as_filetype(MF_KEYS_FILE, datain, keyCount * MF_KEY_LENGTH, RDV40_SPIFFS_SAFETY_SAFE); + if (g_dbglevel >= DBG_ERROR) Dbprintf("Loaded %u keys from spiffs file: %s", keyCount, MF_KEYS_FILE); } #endif From 34883cf91ff6b383474cb25fb04f56e00d720788 Mon Sep 17 00:00:00 2001 From: Piotr Rzeszut Date: Thu, 26 Dec 2024 03:03:48 +0100 Subject: [PATCH 11/18] Remove unused pointers to statically alocated storage for keys from pmflash.h. iClass flash-stores password dictionary seem not to be used anywhere. --- include/pmflash.h | 36 +++--------------------------------- 1 file changed, 3 insertions(+), 33 deletions(-) diff --git a/include/pmflash.h b/include/pmflash.h index 027e2fa80..435099fa0 100644 --- a/include/pmflash.h +++ b/include/pmflash.h @@ -26,9 +26,6 @@ // // 0x3F000 - 1 4kb sector = signature // 0x3E000 - 1 4kb sector = settings -// 0x3D000 - 1 4kb sector = default T55XX keys dictionary -// 0x3B000 - 1 4kb sector = default ICLASS keys dictionary -// 0x35000 - 6 4kb sectors = default MFC keys dictionary // #ifndef FLASH_MEM_BLOCK_SIZE # define FLASH_MEM_BLOCK_SIZE 256 @@ -75,45 +72,18 @@ # define T55XX_CONFIG_OFFSET_P(p64k) (FLASH_MEM_MAX_4K_SECTOR_P(p64k) - 0x2000) #endif -// Reserved space for T55XX PWD = 4 kb +// T55XX PWD stored in spiffs #define T55XX_KEYS_FILE "dict_t55xx.bin" #define T55XX_KEY_LENGTH 4 -#ifndef DEFAULT_T55XX_KEYS_OFFSET -# define DEFAULT_T55XX_KEYS_LEN (0x1000) -# define DEFAULT_T55XX_KEYS_OFFSET (T55XX_CONFIG_OFFSET - DEFAULT_T55XX_KEYS_LEN) -# define DEFAULT_T55XX_KEYS_MAX ((DEFAULT_T55XX_KEYS_LEN - 2) / 4) -#endif -#ifndef DEFAULT_T55XX_KEYS_OFFSET_P -# define DEFAULT_T55XX_KEYS_OFFSET_P(p64k) (T55XX_CONFIG_OFFSET_P(p64k) - DEFAULT_T55XX_KEYS_LEN) -#endif - -// Reserved space for iClass keys = 4 kb +// iClass keys stored in spiffs #define ICLASS_KEYS_FILE "dict_iclass.bin" #define ICLASS_KEY_LENGTH 8 -#ifndef DEFAULT_ICLASS_KEYS_OFFSET -# define DEFAULT_ICLASS_KEYS_LEN (0x1000) -# define DEFAULT_ICLASS_KEYS_OFFSET (DEFAULT_T55XX_KEYS_OFFSET - DEFAULT_ICLASS_KEYS_LEN) -# define DEFAULT_ICLASS_KEYS_MAX ((DEFAULT_ICLASS_KEYS_LEN - 2) / 8) -#endif -#ifndef DEFAULT_ICLASS_KEYS_OFFSET_P -# define DEFAULT_ICLASS_KEYS_OFFSET_P(p64k) (DEFAULT_T55XX_KEYS_OFFSET_P(p64k) - DEFAULT_ICLASS_KEYS_LEN) -#endif - -// Reserved space for MIFARE Keys = 24 kb +// Mifare keys stored in spiffs #define MF_KEYS_FILE "dict_mf.bin" #define MF_KEY_LENGTH 6 -#ifndef DEFAULT_MF_KEYS_OFFSET -# define DEFAULT_MF_KEYS_LEN (0x6000) -# define DEFAULT_MF_KEYS_OFFSET (DEFAULT_ICLASS_KEYS_OFFSET - DEFAULT_MF_KEYS_LEN) -# define DEFAULT_MF_KEYS_MAX ((DEFAULT_MF_KEYS_LEN - 2) / 6) -#endif -#ifndef DEFAULT_MF_KEYS_OFFSET_P -# define DEFAULT_MF_KEYS_OFFSET_P(p64k) (DEFAULT_ICLASS_KEYS_OFFSET_P(p64k) - DEFAULT_MF_KEYS_LEN) -#endif - // RDV40, validation structure to help identifying that client/firmware is talking with RDV40 typedef struct { uint8_t magic[4]; From 90b8c1d39ee0549d5d6af4c87ef5550481856c07 Mon Sep 17 00:00:00 2001 From: Piotr Rzeszut Date: Thu, 26 Dec 2024 03:08:17 +0100 Subject: [PATCH 12/18] Update documentation & changelog to reflect key dictionaries moved to SPIFFS from statically allocated memory --- CHANGELOG.md | 1 + doc/ext_flash_notes.md | 14 +------------- 2 files changed, 2 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 97acdd258..ab665b504 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ All notable changes to this project will be documented in this file. This project uses the changelog in accordance with [keepchangelog](http://keepachangelog.com/). Please use this to write notable changes, which is not the same as git commit log... ## [unreleased][unreleased] +- move flash-stored key dictionaries (Mifare, iClass, T55XX) to SPIFFS files (@piotrva) - Added support for connection to host device in all Docker envs (@doegox) - Changed `hf 15 info` to show all type matches and check ST25TVxC signature (@doegox) - Added initial support for ST25TN and its signature verification (@doegox) diff --git a/doc/ext_flash_notes.md b/doc/ext_flash_notes.md index 6a86c7778..42a5d0829 100644 --- a/doc/ext_flash_notes.md +++ b/doc/ext_flash_notes.md @@ -50,7 +50,7 @@ Page 2: * to erase it: `mem wipe p 2` Page 3: -* used by Proxmark3 RDV4 specific functions: flash signature and keys dictionaries, see below for details +* used by Proxmark3 RDV4 specific functions: flash signature and configurations, see below for details * to dump it: `mem dump -f page3_dump -o 196608 -l 65536` * to erase it: * **Beware** it will erase your flash signature so better to back it up first as you won't be able to regenerate it by yourself! @@ -62,18 +62,6 @@ Page 3: Page3 is used as follows by the Proxmark3 RDV4 firmware: -* **MF_KEYS** - * offset: page 3 sector 5 (0x5) @ 3*0x10000+5*0x1000=0x35000 - * length: 6 sectors - -* **ICLASS_KEYS** - * offset: page 3 sector 11 (0xB) @ 3*0x10000+11*0x1000=0x3B000 - * length: 1 sector - -* **T55XX_KEYS** - * offset: page 3 sector 12 (0xC) @ 3*0x10000+12*0x1000=0x3C000 - * length: 1 sector - * **T55XX_CONFIG** * offset: page 3 sector 13 (0xD) @ 3*0x10000+13*0x1000=0x3D000 * length: 1 sector (actually only a few bytes are used to store `t55xx_config` structure) From 5ed4804044d6b4ee58ddd9905221b3d7fa381c9e Mon Sep 17 00:00:00 2001 From: Piotr Rzeszut Date: Thu, 26 Dec 2024 04:10:10 +0100 Subject: [PATCH 13/18] Fix missing header file for strcpy_s --- client/src/cmdflashmem.c | 1 + 1 file changed, 1 insertion(+) diff --git a/client/src/cmdflashmem.c b/client/src/cmdflashmem.c index 3489813ef..fbc8c1fc7 100644 --- a/client/src/cmdflashmem.c +++ b/client/src/cmdflashmem.c @@ -17,6 +17,7 @@ //----------------------------------------------------------------------------- #include "cmdflashmem.h" #include +#include #include "cmdparser.h" // command_t #include "cliparser.h" #include "pmflash.h" // rdv40validation_t From 4b5d532c9ad4843b0ce94c40dbd1ef8a406c0134 Mon Sep 17 00:00:00 2001 From: Piotr Rzeszut Date: Thu, 26 Dec 2024 04:13:03 +0100 Subject: [PATCH 14/18] Revert to strcpy instead of strcpy_s for compatibility. --- client/src/cmdflashmem.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client/src/cmdflashmem.c b/client/src/cmdflashmem.c index fbc8c1fc7..dbb030bf8 100644 --- a/client/src/cmdflashmem.c +++ b/client/src/cmdflashmem.c @@ -259,7 +259,7 @@ static int CmdFlashMemLoad(const char *Cmd) { free(data); return PM3_EOVFLOW; } - strcpy_s(spiffsDest, 32, MF_KEYS_FILE); + strcpy(spiffsDest, MF_KEYS_FILE); break; case DICTIONARY_T55XX: keylen = T55XX_KEY_LENGTH; @@ -273,7 +273,7 @@ static int CmdFlashMemLoad(const char *Cmd) { free(data); return PM3_EOVFLOW; } - strcpy_s(spiffsDest, 32, T55XX_KEYS_FILE); + strcpy(spiffsDest, T55XX_KEYS_FILE); break; case DICTIONARY_ICLASS: keylen = ICLASS_KEY_LENGTH; @@ -287,7 +287,7 @@ static int CmdFlashMemLoad(const char *Cmd) { free(data); return PM3_EOVFLOW; } - strcpy_s(spiffsDest, 32, ICLASS_KEYS_FILE); + strcpy(spiffsDest, ICLASS_KEYS_FILE); break; case DICTIONARY_NONE: res = loadFile_safe(filename, ".bin", (void **)&data, &datalen); From dd17effaab630110204181dca7a7c58602957c20 Mon Sep 17 00:00:00 2001 From: Piotr Rzeszut Date: Thu, 26 Dec 2024 19:46:55 +0100 Subject: [PATCH 15/18] Move T55XX config to spiffs file --- armsrc/lfops.c | 47 +++++++++++++---------------------------------- include/pmflash.h | 7 +------ 2 files changed, 14 insertions(+), 40 deletions(-) diff --git a/armsrc/lfops.c b/armsrc/lfops.c index a0d3ff09c..6731cba86 100644 --- a/armsrc/lfops.c +++ b/armsrc/lfops.c @@ -325,33 +325,9 @@ void setT55xxConfig(uint8_t arg0, const t55xx_configurations_t *c) { return; } - if (!FlashInit()) { - BigBuf_free(); - return; - } + rdv40_spiffs_write(T55XX_CONFIG_FILE, (uint8_t*)&T55xx_Timing, T55XX_CONFIG_LEN, RDV40_SPIFFS_SAFETY_SAFE); - uint8_t *buf = BigBuf_malloc(T55XX_CONFIG_LEN); - Flash_CheckBusy(BUSY_TIMEOUT); - uint16_t res = Flash_ReadDataCont(T55XX_CONFIG_OFFSET, buf, T55XX_CONFIG_LEN); - if (res == 0) { - FlashStop(); - BigBuf_free(); - return; - } - - memcpy(buf, &T55xx_Timing, T55XX_CONFIG_LEN); - - // delete old configuration - Flash_CheckBusy(BUSY_TIMEOUT); - Flash_WriteEnable(); - Flash_Erase4k(3, 0xD); - - // write new - res = Flash_Write(T55XX_CONFIG_OFFSET, buf, T55XX_CONFIG_LEN); - - if (res == T55XX_CONFIG_LEN && g_dbglevel > 1) { - DbpString("T55XX Config save " _GREEN_("success")); - } + DbpString("T55XX Config save " _GREEN_("success")); BigBuf_free(); #endif @@ -364,15 +340,17 @@ t55xx_configurations_t *getT55xxConfig(void) { void loadT55xxConfig(void) { #ifdef WITH_FLASH - if (!FlashInit()) { - return; - } - uint8_t *buf = BigBuf_malloc(T55XX_CONFIG_LEN); - Flash_CheckBusy(BUSY_TIMEOUT); - uint16_t isok = Flash_ReadDataCont(T55XX_CONFIG_OFFSET, buf, T55XX_CONFIG_LEN); - FlashStop(); + uint32_t size = 0; + if (exists_in_spiffs(T55XX_CONFIG_FILE)) { + size = size_in_spiffs(T55XX_CONFIG_FILE); + } + if (size == 0) { + Dbprintf("Spiffs file: %s does not exists or empty.", T55XX_CONFIG_FILE); + BigBuf_free(); + return; + } // verify read mem is actual data. uint8_t cntA = T55XX_CONFIG_LEN, cntB = T55XX_CONFIG_LEN; @@ -381,6 +359,7 @@ void loadT55xxConfig(void) { if (buf[i] == 0x00) cntB--; } if (!cntA || !cntB) { + Dbprintf("Spiffs file: %s does not malformed or empty.", T55XX_CONFIG_FILE); BigBuf_free(); return; } @@ -388,7 +367,7 @@ void loadT55xxConfig(void) { if (buf[0] != 0xFF) // if not set for clear memcpy((uint8_t *)&T55xx_Timing, buf, T55XX_CONFIG_LEN); - if (isok == T55XX_CONFIG_LEN) { + if (size == T55XX_CONFIG_LEN) { if (g_dbglevel > 1) DbpString("T55XX Config load success"); } diff --git a/include/pmflash.h b/include/pmflash.h index 435099fa0..67f5b350a 100644 --- a/include/pmflash.h +++ b/include/pmflash.h @@ -65,12 +65,7 @@ # define T55XX_CONFIG_LEN sizeof( t55xx_configurations_t ) #endif -#ifndef T55XX_CONFIG_OFFSET -# define T55XX_CONFIG_OFFSET (FLASH_MEM_MAX_4K_SECTOR - 0x2000) -#endif -#ifndef T55XX_CONFIG_OFFSET_P -# define T55XX_CONFIG_OFFSET_P(p64k) (FLASH_MEM_MAX_4K_SECTOR_P(p64k) - 0x2000) -#endif +#define T55XX_CONFIG_FILE "cfg_t55xx.bin" // T55XX PWD stored in spiffs #define T55XX_KEYS_FILE "dict_t55xx.bin" From 526110609836a90040dfd303bad9edf629fc1988 Mon Sep 17 00:00:00 2001 From: Piotr Rzeszut Date: Thu, 26 Dec 2024 21:58:30 +0100 Subject: [PATCH 16/18] Add spiffs operation checks, update changelog and documentation after moving t55xx configurations --- CHANGELOG.md | 2 +- armsrc/lfops.c | 21 +++++++++++++++------ armsrc/mifarecmd.c | 9 ++++++--- doc/ext_flash_notes.md | 12 ++++-------- 4 files changed, 26 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 86aa37671..ff0985fd4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ All notable changes to this project will be documented in this file. This project uses the changelog in accordance with [keepchangelog](http://keepachangelog.com/). Please use this to write notable changes, which is not the same as git commit log... ## [unreleased][unreleased] -- Changed flash-stored key dictionaries (Mifare, iClass, T55XX) to SPIFFS files (@piotrva) +- Changed flash-stored key dictionaries (Mifare, iClass, T55XX) and T55XX configurations to SPIFFS files (@piotrva) - Added `hf iclass trbl` to perform tear-off attacks on iClass (@antiklesys) - Added support for connection to host device in all Docker envs (@doegox) - Changed `hf 15 info` to show all type matches and check ST25TVxC signature (@doegox) diff --git a/armsrc/lfops.c b/armsrc/lfops.c index 6731cba86..c93880944 100644 --- a/armsrc/lfops.c +++ b/armsrc/lfops.c @@ -325,9 +325,9 @@ void setT55xxConfig(uint8_t arg0, const t55xx_configurations_t *c) { return; } - rdv40_spiffs_write(T55XX_CONFIG_FILE, (uint8_t*)&T55xx_Timing, T55XX_CONFIG_LEN, RDV40_SPIFFS_SAFETY_SAFE); - - DbpString("T55XX Config save " _GREEN_("success")); + if (SPIFFS_OK == rdv40_spiffs_write(T55XX_CONFIG_FILE, (uint8_t*)&T55xx_Timing, T55XX_CONFIG_LEN, RDV40_SPIFFS_SAFETY_SAFE)) { + DbpString("T55XX Config save " _GREEN_("success")); + } BigBuf_free(); #endif @@ -352,6 +352,12 @@ void loadT55xxConfig(void) { return; } + if (SPIFFS_OK != rdv40_spiffs_read(T55XX_CONFIG_FILE, buf, T55XX_CONFIG_LEN, RDV40_SPIFFS_SAFETY_SAFE)) { + Dbprintf("Spiffs file: %s cannot be read.", T55XX_CONFIG_FILE); + BigBuf_free(); + return; + } + // verify read mem is actual data. uint8_t cntA = T55XX_CONFIG_LEN, cntB = T55XX_CONFIG_LEN; for (int i = 0; i < T55XX_CONFIG_LEN; i++) { @@ -2148,9 +2154,12 @@ void T55xx_ChkPwds(uint8_t flags, bool ledcontrol) { // adjust available pwd_count pwd_count = pwd_size_available / T55XX_KEY_LENGTH; - rdv40_spiffs_read_as_filetype(T55XX_KEYS_FILE, pwds, pwd_size_available, RDV40_SPIFFS_SAFETY_SAFE); - - if (g_dbglevel >= DBG_ERROR) Dbprintf("Loaded %u passwords from spiffs file: %s", pwd_count, T55XX_KEYS_FILE); + if (SPIFFS_OK == rdv40_spiffs_read_as_filetype(T55XX_KEYS_FILE, pwds, pwd_size_available, RDV40_SPIFFS_SAFETY_SAFE)) { + if (g_dbglevel >= DBG_ERROR) Dbprintf("Loaded %u passwords from spiffs file: %s", pwd_count, T55XX_KEYS_FILE); + } else { + Dbprintf("Spiffs file: %s cannot be read.", T55XX_KEYS_FILE); + goto OUT; + } #endif diff --git a/armsrc/mifarecmd.c b/armsrc/mifarecmd.c index b59b17a79..bf308364d 100644 --- a/armsrc/mifarecmd.c +++ b/armsrc/mifarecmd.c @@ -1924,9 +1924,12 @@ void MifareChkKeys_fast(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *da if (datain == NULL) goto OUT; - rdv40_spiffs_read_as_filetype(MF_KEYS_FILE, datain, keyCount * MF_KEY_LENGTH, RDV40_SPIFFS_SAFETY_SAFE); - - if (g_dbglevel >= DBG_ERROR) Dbprintf("Loaded %u keys from spiffs file: %s", keyCount, MF_KEYS_FILE); + if (SPIFFS_OK == rdv40_spiffs_read_as_filetype(MF_KEYS_FILE, datain, keyCount * MF_KEY_LENGTH, RDV40_SPIFFS_SAFETY_SAFE)) { + if (g_dbglevel >= DBG_ERROR) Dbprintf("Loaded %u keys from spiffs file: %s", keyCount, MF_KEYS_FILE); + } else { + Dbprintf("Spiffs file: %s cannot be read.", MF_KEYS_FILE); + goto OUT; + } } #endif diff --git a/doc/ext_flash_notes.md b/doc/ext_flash_notes.md index 42a5d0829..d54c2be53 100644 --- a/doc/ext_flash_notes.md +++ b/doc/ext_flash_notes.md @@ -37,20 +37,20 @@ Therefore a flash address can be interpreted as such: Page 0: * available for user data * to dump it: `mem dump -f page0_dump -o 0 -l 65536` -* to erase it: `mem wipe p 0` +* to erase it: `mem wipe -p 0` Page 1: * available for user data * to dump it: `mem dump -f page1_dump -o 65536 -l 65536` -* to erase it: `mem wipe p 1` +* to erase it: `mem wipe -p 1` Page 2: * available for user data * to dump it: `mem dump -f page2_dump -o 131072 -l 65536` -* to erase it: `mem wipe p 2` +* to erase it: `mem wipe -p 2` Page 3: -* used by Proxmark3 RDV4 specific functions: flash signature and configurations, see below for details +* used by Proxmark3 RDV4 specific functions: flash signature, see below for details * to dump it: `mem dump -f page3_dump -o 196608 -l 65536` * to erase it: * **Beware** it will erase your flash signature so better to back it up first as you won't be able to regenerate it by yourself! @@ -62,10 +62,6 @@ Page 3: Page3 is used as follows by the Proxmark3 RDV4 firmware: -* **T55XX_CONFIG** - * offset: page 3 sector 13 (0xD) @ 3*0x10000+13*0x1000=0x3D000 - * length: 1 sector (actually only a few bytes are used to store `t55xx_config` structure) - * **RSA SIGNATURE**, see below for details * offset: page 3 sector 15 (0xF) offset 0xF7F @ 3*0x10000+15*0x1000+0xF7F=0x3FF7F (decimal 262015) * length: 128 bytes From 69606aeb0720b98a730fa56a9471f8cfe3469211 Mon Sep 17 00:00:00 2001 From: Piotr Rzeszut Date: Fri, 27 Dec 2024 00:28:07 +0100 Subject: [PATCH 17/18] Changed `lf em 410x sim` to use default gap value of 0 and extended help, addressing #2197 --- CHANGELOG.md | 1 + client/src/cmdlfem410x.c | 10 ++++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ebbd7529f..8e4086b8b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ All notable changes to this project will be documented in this file. This project uses the changelog in accordance with [keepchangelog](http://keepachangelog.com/). Please use this to write notable changes, which is not the same as git commit log... ## [unreleased][unreleased] +- Changed `lf em 410x sim` to use default gap value of 0 and extended help (@piotrva) - Added `hf iclass trbl` to perform tear-off attacks on iClass (@antiklesys) - Added support for connection to host device in all Docker envs (@doegox) - Changed `hf 15 info` to show all type matches and check ST25TVxC signature (@doegox) diff --git a/client/src/cmdlfem410x.c b/client/src/cmdlfem410x.c index 10ad41369..5abbbd4d3 100644 --- a/client/src/cmdlfem410x.c +++ b/client/src/cmdlfem410x.c @@ -449,17 +449,19 @@ static int CmdEM410xSim(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "lf em 410x sim", "Enables simulation of EM 410x card.\n" - "Simulation runs until the button is pressed or another USB command is issued.", + "Simulation runs until the button is pressed or another USB command is issued.\n" + "Most common readers expects the code to be sent in loop without a break (i.e. --gap 0).\n" + "For other, more advanced readers there might be a need to set a non-zero gap value.", "lf em 410x sim --id 0F0368568B\n" "lf em 410x sim --id 0F0368568B --clk 32\n" - "lf em 410x sim --id 0F0368568B --gap 0" + "lf em 410x sim --id 0F0368568B --gap 20" ); void *argtable[] = { arg_param_begin, arg_u64_0(NULL, "clk", "", "<32|64> clock (default 64)"), arg_str1(NULL, "id", "", "EM Tag ID number (5 hex bytes)"), - arg_u64_0(NULL, "gap", "", "gap (0's) between ID repeats (default 20)"), + arg_u64_0(NULL, "gap", "", "gap (0's) between ID repeats (default 0)"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -467,7 +469,7 @@ static int CmdEM410xSim(const char *Cmd) { // clock is 64 in EM410x tags int clk = arg_get_u32_def(ctx, 1, 64); int uid_len = 0; - int gap = arg_get_u32_def(ctx, 3, 20); + int gap = arg_get_u32_def(ctx, 3, 0); uint8_t uid[5] = {0}; CLIGetHexWithReturn(ctx, 2, uid, &uid_len); CLIParserFree(ctx); From afcd57b1a18e05f7afdb2fd10a95f5c99a94acd3 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Sat, 28 Dec 2024 19:18:27 +0100 Subject: [PATCH 18/18] identify MIFARE Duox --- CHANGELOG.md | 1 + client/src/cmdhf14a.c | 1 + client/src/cmdhfmfdes.c | 14 +++++++++++++- 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ebbd7529f..72b39ac84 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ All notable changes to this project will be documented in this file. This project uses the changelog in accordance with [keepchangelog](http://keepachangelog.com/). Please use this to write notable changes, which is not the same as git commit log... ## [unreleased][unreleased] +- Changed `hf 14a info` - now identifies MIAFRE Duox (@iceman1001) - Added `hf iclass trbl` to perform tear-off attacks on iClass (@antiklesys) - Added support for connection to host device in all Docker envs (@doegox) - Changed `hf 15 info` to show all type matches and check ST25TVxC signature (@doegox) diff --git a/client/src/cmdhf14a.c b/client/src/cmdhf14a.c index cd1db3e08..03e8585e1 100644 --- a/client/src/cmdhf14a.c +++ b/client/src/cmdhf14a.c @@ -1884,6 +1884,7 @@ static int detect_nxp_card_print(uint8_t sak, uint16_t atqa, uint64_t select_sta printTag("MIFARE DESFire EV2 2K/4K/8K/16K/32K"); printTag("MIFARE DESFire EV3 2K/4K/8K"); printTag("MIFARE DESFire Light 640B"); + printTag("MIFARE Duox"); type |= MTDESFIRE; } else { printTag("MIFARE Plus EV1 2K/4K CL2 in SL3"); diff --git a/client/src/cmdhfmfdes.c b/client/src/cmdhfmfdes.c index ee8b537c9..d0fcbf112 100644 --- a/client/src/cmdhfmfdes.c +++ b/client/src/cmdhfmfdes.c @@ -148,6 +148,7 @@ typedef enum { PLUS_EV2, NTAG413DNA, NTAG424, + DUOX, } nxp_cardtype_t; typedef enum { @@ -272,6 +273,10 @@ static char *getVersionStr(uint8_t type, uint8_t major, uint8_t minor) { snprintf(retStr, sizeof(buf), "%x.%x ( " _GREEN_("Plus EV1") " )", major, minor); else if (type == 0x02 && major == 0x22 && minor == 0x00) snprintf(retStr, sizeof(buf), "%x.%x ( " _GREEN_("Plus EV2") " )", major, minor); + else if (type == 0x01 && major == 0xA0 && minor == 0x00) + snprintf(retStr, sizeof(buf), "%x.%x ( " _GREEN_("DUOX") " )", major, minor); + else if ((type & 0x08) == 0x08) + snprintf(retStr, sizeof(buf), "%x.%x ( " _GREEN_("DESFire Light") " )", major, minor); else snprintf(retStr, sizeof(buf), "%x.%x ( " _YELLOW_("Unknown") " )", major, minor); return buf; @@ -338,6 +343,10 @@ static nxp_cardtype_t getCardType(uint8_t type, uint8_t major, uint8_t minor) { if (type == 0x01 && major == 0x33 && minor == 0x00) return DESFIRE_EV3; + // Duox + if (type == 0x01 && major == 0xA0 && minor == 0x00) + return DUOX; + // DESFire Light if (type == 0x08 && major == 0x30 && minor == 0x00) return DESFIRE_LIGHT; @@ -744,6 +753,8 @@ static int CmdHF14ADesInfo(const char *Cmd) { PrintAndLogEx(INFO, "\t2.2 - DESFire Ev2 XL, Originality check, proximity check, EAL5"); if (major == 3 && minor == 0) PrintAndLogEx(INFO, "\t3.0 - DESFire Ev3, Originality check, proximity check, badass EAL6 ?"); + if (major == 0xA0 && minor == 0) + PrintAndLogEx(INFO, "\tx.x - DUOX, Originality check, proximity check, EAL6++"); if (major == 0 && minor == 2) PrintAndLogEx(INFO, "\t0.2 - DESFire Light, Originality check, "); @@ -761,7 +772,8 @@ static int CmdHF14ADesInfo(const char *Cmd) { if (cardtype == DESFIRE_EV2 || cardtype == DESFIRE_EV2_XL || cardtype == DESFIRE_LIGHT || cardtype == DESFIRE_EV3 || - cardtype == NTAG413DNA) { + cardtype == NTAG413DNA || + cardtype == DUOX) { // Signature originality check uint8_t signature[250] = {0}; // must be 56 size_t signature_len = 0;