From f022a8f96e77e6c2894260e5589e09f6e5b3934e Mon Sep 17 00:00:00 2001 From: astrid rowland Date: Thu, 13 Apr 2023 14:44:03 -0500 Subject: [PATCH 01/62] added new keys from The Horde --- client/dictionaries/mfc_default_keys.dic | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/client/dictionaries/mfc_default_keys.dic b/client/dictionaries/mfc_default_keys.dic index 1cd3b0d79..665757f25 100644 --- a/client/dictionaries/mfc_default_keys.dic +++ b/client/dictionaries/mfc_default_keys.dic @@ -2029,9 +2029,8 @@ D144BD193063 8627C10A7014 453857395635 # -# Data from "the more, the marriott" mifare project (colonel borkmundus) -# -# Isn't theirs Saflok ? +# Data from "the more the marriott" mifare project (colonelborkmundus) +# aka The Horde # # 20230125-01, Elite Member Marriott Rewards 43012BD9EB87 @@ -2098,6 +2097,15 @@ C49DAE1C6049 6E029927600D 3E173F64C01C C670A9AD6066 +# 20230413-69, Westin +487339FA02E0 +# 20230413-70, Marriott Bonvoy +DBD5CA4EE467 +A0B1F234006C +180DE12B700E +# 20230413-71, Westin +1352C68F7A56 +# # # Food GEM 6686FADE5566 From 17aa99b0c70adf489765efc7af458fb2165265c6 Mon Sep 17 00:00:00 2001 From: astrid rowland Date: Thu, 13 Apr 2023 17:44:55 -0500 Subject: [PATCH 02/62] added new keys from The Horde, badges 76-94 --- client/dictionaries/mfc_default_keys.dic | 37 ++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/client/dictionaries/mfc_default_keys.dic b/client/dictionaries/mfc_default_keys.dic index 665757f25..4af8cf64a 100644 --- a/client/dictionaries/mfc_default_keys.dic +++ b/client/dictionaries/mfc_default_keys.dic @@ -2105,6 +2105,43 @@ A0B1F234006C 180DE12B700E # 20230413-71, Westin 1352C68F7A56 +# 20230413-76, Ritz Carlton +318BD98C1CEF +# 20230413-77, Marriott +D23C1CB1216E +# 20230413-78, Caesars +A1D92F808CAF +# 20230413-79, The Cosmopolitan, Vegas +96A301BCE267 +# 20230413-80, Aria +1153C319B4F8 +# 20230413-81, Aria +110C819BBEF8 +# 20230413-82, Aria +1332117E8756 +# 20230413-83, Kimpton +500AE915F50A +5032E362B484 +8B63AB712753 +# 20230413-85, Kimpton +06106E187106 +2E45C23DC541 +D9FF8BEE7550 +# 20230413-87, Marriott +42F7A186BF87 +# 20230413-88, Meritage Resort +D213B093B79A +# 20230413-89, Meritage Resort +216024C49EDF +# 20230413-90, Gaylord Palms +D201DBB6AB6E +# 20230413-91, Residence Inn +9F4AD875BB30 +# 20230413-92, Marriott +3352DB1E8777 +# 20230413-94, Marriott +09074A146605 +151F3E85EC46 # # # Food GEM From ba3d5c32ae7abd652f016b25509a9b205edb1191 Mon Sep 17 00:00:00 2001 From: Self Not Found Date: Sun, 16 Apr 2023 09:51:07 +0800 Subject: [PATCH 03/62] Fix logic --- client/src/cmdhfmfp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/cmdhfmfp.c b/client/src/cmdhfmfp.c index 7646953e8..7c45455f4 100644 --- a/client/src/cmdhfmfp.c +++ b/client/src/cmdhfmfp.c @@ -1024,7 +1024,7 @@ static int MFPKeyCheck(uint8_t startSector, uint8_t endSector, uint8_t startKeyA for (int retry = 0; retry < 4; retry++) { res = MifareAuth4(NULL, keyn, keyList[i], selectCard, true, false, false, true); - if (res == PM3_SUCCESS || PM3_EWRONGANSWER) + if (res == PM3_SUCCESS || res == PM3_EWRONGANSWER) break; if (verbose) From 03070778a2aae68bdc1c08028f692944c10f1a0d Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Sun, 16 Apr 2023 05:58:34 +0200 Subject: [PATCH 04/62] mfp chk - allow for breaking out at every attempt --- client/src/cmdhfmfp.c | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/client/src/cmdhfmfp.c b/client/src/cmdhfmfp.c index 7c45455f4..b079e15c1 100644 --- a/client/src/cmdhfmfp.c +++ b/client/src/cmdhfmfp.c @@ -1006,15 +1006,17 @@ static int MFPKeyCheck(uint8_t startSector, uint8_t endSector, uint8_t startKeyA for (uint8_t keyAB = startKeyAB; keyAB <= endKeyAB; keyAB++) { // main cycle with key check for (int i = 0; i < keyListLen; i++) { + + // allow client abort every iteration + if (kbd_enter_pressed()) { + PrintAndLogEx(WARNING, "\naborted via keyboard!\n"); + DropField(); + return PM3_EOPABORTED; + } + if (i % 10 == 0) { - - if (verbose == false) + if (verbose == false) { PrintAndLogEx(NORMAL, "." NOLF); - - if (kbd_enter_pressed()) { - PrintAndLogEx(WARNING, "\naborted via keyboard!\n"); - DropField(); - return PM3_EOPABORTED; } } @@ -1037,9 +1039,6 @@ static int MFPKeyCheck(uint8_t startSector, uint8_t endSector, uint8_t startKeyA msleep(100); } - if (verbose) - PrintAndLogEx(WARNING, "\nsector %02d key %d [%s] res: %d", sector, keyAB, sprint_hex_inrow(keyList[i], 16), res); - // key for [sector,keyAB] found if (res == PM3_SUCCESS) { if (verbose) @@ -1052,9 +1051,14 @@ static int MFPKeyCheck(uint8_t startSector, uint8_t endSector, uint8_t startKeyA DropField(); selectCard = true; msleep(50); + + // break out from keylist check loop, break; } + if (verbose) + PrintAndLogEx(WARNING, "\nsector %02d key %d [%s] res: %d", sector, keyAB, sprint_hex_inrow(keyList[i], 16), res); + // RES can be: // PM3_ERFTRANS -7 // PM3_EWRONGANSWER -16 From 1a3a6e5f1998d0d38aea7f228036fab015aa12c7 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Mon, 17 Apr 2023 20:12:03 +0200 Subject: [PATCH 05/62] added no save to t55xx dump. and some minor text output style --- CHANGELOG.md | 1 + client/src/cmdlft55xx.c | 37 ++++++++++++++++++++++++------------- 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f57df0761..8ff678161 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 t55xx dump --ns` - now supports `no save` of memory (@iceman1001) - Fixed the USB enumeration process (@wh201906) - Fixed `hf mf rdsc` - now correctly gets size in bytes when sector is larger than 32 (@iceman1001) - Changed `hf mf supercard` - Support editing UID and recovery of keys from second generation card (@AloneLiberty) diff --git a/client/src/cmdlft55xx.c b/client/src/cmdlft55xx.c index f1cb773fb..554658c9f 100644 --- a/client/src/cmdlft55xx.c +++ b/client/src/cmdlft55xx.c @@ -397,7 +397,8 @@ int t55xxWrite(uint8_t block, bool page1, bool usepwd, bool testMode, uint32_t p } void printT5xxHeader(uint8_t page) { - PrintAndLogEx(SUCCESS, "Reading Page %d:", page); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(SUCCESS, "Page " _YELLOW_("%d"), page); PrintAndLogEx(SUCCESS, "blk | hex data | binary | ascii"); PrintAndLogEx(SUCCESS, "----+----------+----------------------------------+-------"); } @@ -815,7 +816,7 @@ static void T55xx_Print_DownlinkMode(uint8_t downlink_mode) { break; } - PrintAndLogEx(NORMAL, msg); + PrintAndLogEx(SUCCESS, msg); } static int CmdT55xxWakeUp(const char *Cmd) { @@ -2222,14 +2223,15 @@ static int CmdT55xxDump(const char *Cmd) { "lf t55xx dump -f my_lf_dump" ); - // 1 (help) + 3 (two user specified params) + (5 T55XX_DLMODE_SINGLE) - void *argtable[4 + 5] = { + // 1 (help) + 4 (two user specified params) + (5 T55XX_DLMODE_SINGLE) + void *argtable[5 + 5] = { arg_param_begin, arg_str0("f", "file", "", "filename (default is generated on blk 0)"), arg_lit0("o", "override", "override, force pwd read despite danger to card"), arg_str0("p", "pwd", "", "password (4 hex bytes)"), + arg_lit0(NULL, "ns", "no save"), }; - uint8_t idx = 4; + uint8_t idx = 5; arg_add_t55xx_downloadlink(argtable, &idx, T55XX_DLMODE_SINGLE, T55XX_DLMODE_SINGLE); CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -2251,10 +2253,12 @@ static int CmdT55xxDump(const char *Cmd) { usepwd = true; } - bool r0 = arg_get_lit(ctx, 4); - bool r1 = arg_get_lit(ctx, 5); - bool r2 = arg_get_lit(ctx, 6); - bool r3 = arg_get_lit(ctx, 7); + bool nosave = arg_get_lit(ctx, 4); + + bool r0 = arg_get_lit(ctx, 5); + bool r1 = arg_get_lit(ctx, 6); + bool r2 = arg_get_lit(ctx, 7); + bool r3 = arg_get_lit(ctx, 8); CLIParserFree(ctx); if ((r0 + r1 + r2 + r3) > 1) { @@ -2278,8 +2282,9 @@ static int CmdT55xxDump(const char *Cmd) { // will save the dump file if ALL page 0 is OK printT5xxHeader(0); for (uint8_t i = 0; i < 8; ++i) { - if (T55xxReadBlock(i, 0, usepwd, override, password, downlink_mode) != PM3_SUCCESS) + if (T55xxReadBlock(i, 0, usepwd, override, password, downlink_mode) != PM3_SUCCESS) { success = false; + } // only show override warning on the first block read if (override == 1) { @@ -2287,12 +2292,14 @@ static int CmdT55xxDump(const char *Cmd) { } } printT5xxHeader(1); - for (uint8_t i = 0; i < 4; i++) - if (T55xxReadBlock(i, 1, usepwd, override, password, downlink_mode) != PM3_SUCCESS) + for (uint8_t i = 0; i < 4; i++) { + if (T55xxReadBlock(i, 1, usepwd, override, password, downlink_mode) != PM3_SUCCESS) { T55x7_SaveBlockData(8 + i, 0x00); + } + } // all ok, save dump to file - if (success) { + if (success && nosave == false) { // set default filename, if not set by user if (strlen(filename) == 0) { @@ -4019,7 +4026,11 @@ static int CmdT55xxSniff(const char *Cmd) { // setup and sample data from Proxmark // if not directed to existing sample/graphbuffer if (use_graphbuf == false) { + + // make loop to call sniff with skip samples.. + // then build it up by adding CmdLFSniff(""); + } // Headings From 3bc472c9bba0bbb8cbd868eb36fa2d58fd5a5589 Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Mon, 17 Apr 2023 23:22:48 +0200 Subject: [PATCH 06/62] hf mf cwipe: use same default ACL as for hf mf wipe --- client/src/mifare/mifarehost.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/client/src/mifare/mifarehost.c b/client/src/mifare/mifarehost.c index 70e601431..9e9f674e1 100644 --- a/client/src/mifare/mifarehost.c +++ b/client/src/mifare/mifarehost.c @@ -1042,7 +1042,8 @@ int mfCWipe(uint8_t *uid, const uint8_t *atqa, const uint8_t *sak) { uint8_t block0[16] = {0x00, 0x56, 0x78, 0xBB, 0x95, 0x08, 0x04, 0x00, 0x02, 0xB2, 0x1E, 0x24, 0x23, 0x27, 0x1E, 0x1D}; // uint8_t block0[16] = {0x04, 0x03, 0x02, 0x01, 0x04, 0x08, 0x04, 0x00, 0x64, 0xB9, 0x95, 0x11, 0x4D, 0x20, 0x42, 0x09}; uint8_t blockD[16] = {0x00}; - uint8_t blockK[16] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x08, 0x77, 0x8F, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + // default transport ACL + uint8_t blockK[16] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x07, 0x80, 0x69, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; uint8_t params = MAGIC_SINGLE; if (uid != NULL) { From ed9cdebbfaf20048aea544cf9b77ed019ac9cf03 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Wed, 19 Apr 2023 02:02:20 +0200 Subject: [PATCH 07/62] remove some debug statement --- armsrc/mifarecmd.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/armsrc/mifarecmd.c b/armsrc/mifarecmd.c index 90dbf2bfc..30558541d 100644 --- a/armsrc/mifarecmd.c +++ b/armsrc/mifarecmd.c @@ -1194,8 +1194,6 @@ void MifareAcquireEncryptedNonces(uint32_t arg0, uint32_t arg1, uint32_t flags, reply_old(CMD_ACK, isOK, cuid, num_nonces, buf, sizeof(buf)); LED_B_OFF(); - if (g_dbglevel >= DBG_ERROR) DbpString("AcquireEncryptedNonces finished"); - if (field_off) { FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LEDsoff(); From 6950344567283501faff59a4733d7a0773154b67 Mon Sep 17 00:00:00 2001 From: Davi Mikael <31720422+penegui@users.noreply.github.com> Date: Wed, 19 Apr 2023 01:39:18 -0300 Subject: [PATCH 08/62] Adjusting hf_msdsal standalone module --- armsrc/Standalone/hf_msdsal.c | 31 +++++++++++++++++++------------ armsrc/iso14443a.c | 15 ++++++++++++++- 2 files changed, 33 insertions(+), 13 deletions(-) diff --git a/armsrc/Standalone/hf_msdsal.c b/armsrc/Standalone/hf_msdsal.c index 55c545489..64db3a446 100644 --- a/armsrc/Standalone/hf_msdsal.c +++ b/armsrc/Standalone/hf_msdsal.c @@ -168,7 +168,7 @@ void RunMod(void) { //Expiration date: 17/11 //Service code: 201 //Discretionary data: 0000030000991 - //char token[19] = {0x44,0x12,0x34,0x56,0x05,0x78,0x12,0x34,0xd1,0x71,0x12,0x01,0x00,0x00,0x03,0x00,0x00,0x99,0x1f}; + //char token[19] = {0x44,0x12,0x34,0x56,0x05,0x78,0x56,0x78,0xd1,0x71,0x12,0x01,0x00,0x00,0x03,0x00,0x00,0x99,0x1f}; // // It is possible to initialize directly the emulation mode, having "token" with data and set "chktoken" = true ;) // @@ -185,11 +185,11 @@ void RunMod(void) { // in case there is a read command received we shouldn't break uint8_t data[PM3_CMD_DATA_SIZE] = {0x00}; - uint8_t visauid[7] = {0x01, 0x02, 0x03, 0x04}; + uint8_t visauid[7] = {0x05, 0x06, 0x07, 0x08}; memcpy(data, visauid, 4); // to initialize the emulation - uint8_t tagType = 4; // 4 = ISO/IEC 14443-4 - javacard (JCOP) + uint8_t tagType = 11; // 11 = ISO/IEC 14443-4 - javacard (JCOP) tag_response_info_t *responses; uint32_t cuid = 0; uint32_t counters[3] = { 0x00, 0x00, 0x00 }; @@ -376,7 +376,8 @@ void RunMod(void) { // dynamic_response_info will be in charge of responses dynamic_response_info.response_n = 0; - + + //Dbprintf("receivedCmd: %02x\n", receivedCmd); // received a REQUEST if (receivedCmd[0] == ISO14443A_CMD_REQA && len == 1) { odd_reply = !odd_reply; @@ -386,30 +387,35 @@ void RunMod(void) { // received a HALT } else if (receivedCmd[0] == ISO14443A_CMD_HALT && len == 4) { -// DbpString(_YELLOW_("+") "Received a HALT"); + //DbpString(_YELLOW_("+") "Received a HALT"); p_response = NULL; // received a WAKEUP } else if (receivedCmd[0] == ISO14443A_CMD_WUPA && len == 1) { -// DbpString(_YELLOW_("+") "WAKEUP Received"); + //DbpString(_YELLOW_("+") "WAKEUP Received"); prevCmd = 0; p_response = &responses[RESP_INDEX_ATQA]; // received request for UID (cascade 1) } else if (receivedCmd[1] == 0x20 && receivedCmd[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT && len == 2) { -// DbpString(_YELLOW_("+") "Request for UID C1"); - p_response = &responses[RESP_INDEX_UIDC1]; + //DbpString(_YELLOW_("+") "Request for UID C1"); + p_response = &responses[RESP_INDEX_UIDC1]; // received a SELECT (cascade 1) } else if (receivedCmd[1] == 0x70 && receivedCmd[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT && len == 9) { -// DbpString(_YELLOW_("+") "Request for SELECT S1"); - p_response = &responses[RESP_INDEX_SAKC1]; + //DbpString(_YELLOW_("+") "Request for SELECT S1"); + p_response = &responses[RESP_INDEX_SAKC1]; // received a RATS request } else if (receivedCmd[0] == ISO14443A_CMD_RATS && len == 4) { -// DbpString(_YELLOW_("+") "Request for RATS"); + DbpString(_YELLOW_("+") "Request for RATS"); prevCmd = 0; - p_response = &responses[RESP_INDEX_RATS]; + //p_response = &responses[RESP_INDEX_RATS]; + + static uint8_t rRATS[] = { 0x13, 0x78, 0x80, 0x72, 0x02, 0x80, 0x31, 0x80, 0x66, 0xb1, 0x84, 0x0c, 0x01, 0x6e, 0x01, 0x83, 0x00, 0x90, 0x00 }; + + memcpy(&dynamic_response_info.response[0], rRATS, sizeof(rRATS)); + dynamic_response_info.response_n = sizeof(rRATS); } else { DbpString(_YELLOW_("[ ") "Card reader command" _YELLOW_(" ]")); @@ -483,6 +489,7 @@ void RunMod(void) { } } } + if (dynamic_response_info.response_n > 0) { DbpString(_GREEN_("[ ") "Proxmark3 answer" _GREEN_(" ]")); Dbhexdump(dynamic_response_info.response_n, dynamic_response_info.response, false); diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index 637215cc3..f38edd00d 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -1151,6 +1151,11 @@ bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data, tag_r sak = 0x20; } break; + case 11: { // ISO/IEC 14443-4 - javacard (JCOP) + rATQA[0] = 0x04; + sak = 0x20; + } + break; default: { if (g_dbglevel >= DBG_ERROR) Dbprintf("Error: unknown tagtype (%d)", tagType); @@ -1183,7 +1188,15 @@ bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data, tag_r // Configure the ATQA and SAK accordingly rATQA[0] &= 0xBF; - rSAKc1[0] = sak & 0xFB; + + if(tagType == 11){ + rSAKc1[0] = sak & 0xFC & 0X70; + DbpString(_YELLOW_("[ ") "Passando no Sak Penegui" _YELLOW_(" ]")); + }else{ + rSAKc1[0] = sak & 0xFB; + //DbpString(_YELLOW_("[ ") "Passando no Sak Antigo" _YELLOW_(" ]")); + } + AddCrc14A(rSAKc1, sizeof(rSAKc1) - 2); *cuid = bytes_to_num(data, 4); From 62b226108daaa489f26604fee8c02b82bd9fa56c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=B0lteri=C5=9F=20Ya=C4=9F=C4=B1ztegin=20Ero=C4=9Flu?= Date: Wed, 19 Apr 2023 13:45:07 +0300 Subject: [PATCH 09/62] doc(macos-macports): refactor macports docs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since mid-2022, arm64 builds of stable releases are working. So its been a while not updating this doc :) Signed-off-by: İlteriş Yağıztegin Eroğlu --- CHANGELOG.md | 1 + ...OS-X-MacPorts-Installation-Instructions.md | 82 +++++++++++-------- 2 files changed, 47 insertions(+), 36 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8ff678161..d69b05a8d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -50,6 +50,7 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac - Added `hf legic einfo` - views emulator menory (@0xdeb) - Changed `hf legic view` - now also print the decoded info of the dump file (@0xdeb) - Now `script run hf_mf_ultimatecard.lua -u` supports 10bytes UID (@alejandro12120) + - Update documentation for installation on macOS with MacPorts (@linuxgemini) ## [Nitride.4.16191][2023-01-29] - Changed `build_all_firmwares.sh` to fit GENERIC 256kb firmware images (@doegox) diff --git a/doc/md/Installation_Instructions/Mac-OS-X-MacPorts-Installation-Instructions.md b/doc/md/Installation_Instructions/Mac-OS-X-MacPorts-Installation-Instructions.md index 6fa5a1754..3ea25c20d 100644 --- a/doc/md/Installation_Instructions/Mac-OS-X-MacPorts-Installation-Instructions.md +++ b/doc/md/Installation_Instructions/Mac-OS-X-MacPorts-Installation-Instructions.md @@ -2,7 +2,6 @@ # Mac OS X - MacPorts automatic installation -

These insturctions won't work on Apple Silicon yet!

An arm64 native build of arm-none-eabi-gcc is still not available (as of 2021-11-26).
# Table of Contents - [Mac OS X - MacPorts automatic installation](#mac-os-x---macports-automatic-installation) @@ -23,70 +22,82 @@ 1. Have MacPorts installed. Visit https://www.macports.org/ for more information. - * MacPorts may require a bit more setup. You first need to set up your PATH variable: - - ```bash - export "/opt/local/bin:/opt/local/sbin:/usr/local/bin:/usr/local/sbin:$PATH" - ``` - - For a somewhat seamless development environment: - - ```bash - export C_INCLUDE_PATH="/opt/local/include" - export CPLUS_INCLUDE_PATH="/opt/local/include" - export LIBRARY_PATH="/opt/local/lib" - export LDFLAGS="-L/opt/local/lib" - export CFLAGS="-I/opt/local/include" - export CPPFLAGS="-isystem/opt/local/include -I/opt/local/include" - ``` - -## Installing latest releases +## Installing stable releases directly ^[Top](#top) -Packaging for latest releases are available on MacPorts with the port name `proxmark3-iceman`, with a variant for PM3GENERIC firmwares available as `+pm3generic`. +Packaging for latest releases are available on MacPorts with the port name [`proxmark3-iceman`](https://ports.macports.org/port/proxmark3-iceman/details/), with a variant for PM3GENERIC firmwares available as `+pm3generic`. Installing is as simple as `sudo port install proxmark3-iceman` and if you want to install for PM3GENERIC, you can run `sudo port install proxmark3-iceman +pm3generic` instead. + ## Build from source ^[Top](#top) These instructions will show how to setup the environment on OSX to the point where you'll be able to clone and compile the repo by yourself, as on Linux, Windows, etc. -1. Have MacPorts installed. See above for details. +1. Have MacPorts installed. Visit https://www.macports.org/ for more information. + + * Since you're going to compile directly; this will require a bit more setup, you first need to set up your PATH variable (we assume your MacPorts prefix is located at its default, which is `/opt/local`) in your shell rc file: + + ```bash + export MACPORTS_PREFIX="/opt/local" + # we assume you'll use GNU coreutils; which is also a required dependency for proxmark3 + # install it with `sudo port install coreutils` + export "$MACPORTS_PREFIX/libexec/gnubin:$MACPORTS_PREFIX/bin:$MACPORTS_PREFIX/sbin:$PATH" + ``` + + For a somewhat seamless development environment, you can use these in your shell rc file: + + ```bash + export C_INCLUDE_PATH="$MACPORTS_PREFIX/include:$C_INCLUDE_PATH" + export CPLUS_INCLUDE_PATH="$MACPORTS_PREFIX/include:$CPLUS_INCLUDE_PATH" + export LIBRARY_PATH="$MACPORTS_PREFIX/lib:$LIBRARY_PATH" + export LDFLAGS="-L$MACPORTS_PREFIX/lib $LDFLAGS" + export CFLAGS="-I$MACPORTS_PREFIX/include $CFLAGS" + export CPPFLAGS="-isystem$MACPORTS_PREFIX/include -I$MACPORTS_PREFIX/include $CPPFLAGS" + export PKG_CONFIG_PATH="$MACPORTS_PREFIX/lib/pkgconfig:$MACPORTS_PREFIX/share/pkgconfig:$PKG_CONFIG_PATH" + ``` 2. Install dependencies: - ``` - sudo port install readline qt5 qt5-qtbase pkgconfig arm-none-eabi-gcc arm-none-eabi-binutils lua52 coreutils openssl@3 + ```bash + sudo port install readline jansson lua52 python311 bzip2 openssl11 arm-none-eabi-gcc arm-none-eabi-binutils coreutils qt5 qt5-qtbase pkgconfig ``` 3. Clamp Python version for pkg-config MacPorts doesn't handle Python version defaults when it comes to pkg-config. So even if you have done: - ``` - sudo port install python39 cython39 + ```bash + sudo port install python311 cython311 - sudo port select --set python python39 # this also makes calls to "python" operate on python3.9 - sudo port select --set python3 python39 - sudo port select --set cython cython39 + sudo port select --set python python311 # this also makes calls to "python" operate on python3.11 + sudo port select --set python3 python311 + sudo port select --set cython cython311 ``` This won't set a default python3.pc (and python3-embed.pc) under the MacPorts pkgconfig includes folder. To fix that, follow these steps: - ``` + ```bash cd /opt/local/lib/pkgconfig - sudo ln -svf python3.pc python-3.9.pc - sudo ln -svf python3-embed.pc python-3.9-embed.pc + sudo ln -svf python3.pc python-3.11.pc + sudo ln -svf python3-embed.pc python-3.11-embed.pc + ``` + + _Or_ you can use a framework definition in your shell rc file: + + ```bash + export MACPORTS_FRAMEWORKS_DIR="$MACPORTS_PREFIX/Library/Frameworks" + export PYTHON_FRAMEWORK_DIR="$MACPORTS_FRAMEWORKS_DIR:/Python.framework/Versions/3.11" + export PKG_CONFIG_PATH="$PYTHON_FRAMEWORK_DIR:$PKG_CONFIG_PATH" ``` 4. (optional) Install makefile dependencies: - ``` - sudo port install recode - sudo port install astyle + ```bash + sudo port install recode astyle ``` @@ -103,7 +114,7 @@ cd proxmark3 Now you're ready to follow the [compilation instructions](/doc/md/Use_of_Proxmark/0_Compilation-Instructions.md). From there, you can follow the original instructions. -_Take extra note to instructions if you don't have a Proxmark3 RDV4 device._ +_Take extra note to instructions if you **don't** have a Proxmark3 RDV4 device._ To flash on OS X, better to enter the bootloader mode manually, else you may experience errors. @@ -125,4 +136,3 @@ If you want to manually select serial port, remember that the Proxmark3 port is ```sh proxmark3 /dev/ttyACM0 => proxmark3 /dev/tty.usbmodemiceman1 ``` - From bcc2b1ee700415eb16d1b0cb0971a9cc04f9b5f1 Mon Sep 17 00:00:00 2001 From: Self Not Found Date: Thu, 20 Apr 2023 13:30:36 +0800 Subject: [PATCH 10/62] Fix changelog reminder --- .github/workflows/rebase.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/rebase.yml b/.github/workflows/rebase.yml index ac6a71242..b1cc0e0c1 100644 --- a/.github/workflows/rebase.yml +++ b/.github/workflows/rebase.yml @@ -9,7 +9,7 @@ jobs: - name: Changelog Reminder uses: peterjgrainger/action-changelog-reminder@v1.2.0 with: - changelog_regex: '/CHANGELOG.md' + changelog_regex: 'CHANGELOG.md' customPrMessage: 'You are welcome to add an entry to the CHANGELOG.md as well' env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 039937e28a6cb6446ee6f0b46c646bac8c20baf4 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Sun, 23 Apr 2023 11:27:12 +0200 Subject: [PATCH 11/62] added the possibility to load .MCT dump files in the client. MCT format is a textual one like EML but with extra descriptive lines of text before each sector --- CHANGELOG.md | 1 + client/src/fileutils.c | 97 +++++------------------------------------- client/src/fileutils.h | 13 +++++- 3 files changed, 24 insertions(+), 87 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d69b05a8d..cb3c3e059 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 the possibility to load `.MCT` dump files (@iceman1001) - Changed `lf t55xx dump --ns` - now supports `no save` of memory (@iceman1001) - Fixed the USB enumeration process (@wh201906) - Fixed `hf mf rdsc` - now correctly gets size in bytes when sector is larger than 32 (@iceman1001) diff --git a/client/src/fileutils.c b/client/src/fileutils.c index 8a6049d3c..dfc59b9fb 100644 --- a/client/src/fileutils.c +++ b/client/src/fileutils.c @@ -85,6 +85,8 @@ DumpFileType_t getfiletype(const char *filename) { o = JSON; } else if (str_endswith(s, "dic")) { o = DICTIONARY; + } else if (str_endswith(s, "mct")) { + o = MCT; } else { // mfd, trc, trace is binary o = BIN; @@ -112,27 +114,6 @@ int fileExists(const char *filename) { return result == 0; } -/** - * @brief checks if path is file. - * @param filename - * @return - */ -/* -static bool is_regular_file(const char *filename) { -#ifdef _WIN32 - struct _stat st; - if (_stat(filename, &st) == -1) - return false; -#else - struct stat st; -// stat(filename, &st); - if (lstat(filename, &st) == -1) - return false; -#endif - return S_ISREG(st.st_mode) != 0; -} -*/ - /** * @brief checks if path is directory. * @param filename @@ -152,68 +133,6 @@ static bool is_directory(const char *filename) { return S_ISDIR(st.st_mode) != 0; } -/** - * @brief create a new directory. - * @param dirname - * @return - */ -// Not used... -/* -#ifdef _WIN32 -#define make_dir(a) _mkdir(a) -#else -#define make_dir(a) mkdir(a,0755) //note 0755 MUST have leading 0 for octal linux file permissions -#endif -bool create_path(const char *dirname) { - - if (dirname == NULL) // nothing to do - return false; - - if ((strlen(dirname) == 1) && (dirname[0] == '/')) - return true; - - if ((strlen(dirname) == 2) && (dirname[1] == ':')) - return true; - - if (fileExists(dirname) == 0) { - - char *bs = strrchr(dirname, '\\'); - char *fs = strrchr(dirname, '/'); - - if ((bs == NULL) && (fs != NULL)) { - *fs = 0x00; - create_path (dirname); - *fs = '/'; - } - - if ((bs != NULL) && (fs == NULL)) { - *bs = 0x00; - create_path (dirname); - *bs = '\\'; - } - - if ((bs != NULL) && (fs != NULL)) { - if (strlen (bs) > strlen (fs)) { - *fs = 0x00; // No slash - create_path (dirname); - *fs = '/'; - } else { - *bs = 0x00; - create_path (dirname); - *bs = '\\'; - } - - } - - if (make_dir(dirname) != 0) { - PrintAndLogEx(ERR, "could not create directory.... "_RED_("%s"),dirname); - return false; - } - } - return true; -} -*/ - bool setDefaultPath(savePaths_t pathIndex, const char *path) { if (pathIndex < spItemCount) { @@ -1041,7 +960,8 @@ out: free(fileName); return retval; } -int loadFileEML_safe(const char *preferredName, void **pdata, size_t *datalen) { + +int loadFileMCT_safe(const char *preferredName, void **pdata, size_t *datalen) { char *path; int res = searchFile(&path, RESOURCES_SUBDIR, preferredName, "", false); if (res != PM3_SUCCESS) { @@ -1096,7 +1016,8 @@ int loadFileEML_safe(const char *preferredName, void **pdata, size_t *datalen) { return PM3_EFILE; } - if (line[0] == '#') + // skip lines like "+Sector:" + if (line[0] == '+') continue; strcleanrn(line, sizeof(line)); @@ -1110,7 +1031,7 @@ int loadFileEML_safe(const char *preferredName, void **pdata, size_t *datalen) { } } fclose(f); - PrintAndLogEx(SUCCESS, "loaded " _YELLOW_("%zu") " bytes from text file " _YELLOW_("%s"), counter, preferredName); + PrintAndLogEx(SUCCESS, "loaded " _YELLOW_("%zu") " bytes from MCT file " _YELLOW_("%s"), counter, preferredName); uint8_t *newdump = realloc(*pdata, counter); @@ -2107,6 +2028,10 @@ int pm3_load_dump(const char *fn, void **pdump, size_t *dumplen, size_t maxdumpl PrintAndLogEx(ERR, "Error: Only BIN/EML/JSON formats allowed"); return PM3_EINVARG; } + case MCT: { + res = loadFileMCT_safe(fn, pdump, dumplen); + break; + } } if (res != PM3_SUCCESS) { diff --git a/client/src/fileutils.h b/client/src/fileutils.h index 18e0fbd7e..5a49f529a 100644 --- a/client/src/fileutils.h +++ b/client/src/fileutils.h @@ -56,6 +56,7 @@ typedef enum { EML, JSON, DICTIONARY, + MCT, } DumpFileType_t; int fileExists(const char *filename); @@ -176,9 +177,19 @@ int loadFile_safeEx(const char *preferredName, const char *suffix, void **pdata, * @param datalen the number of bytes loaded from file * @return 0 for ok, 1 for failz */ -int loadFileEML(const char *preferredName, void *data, size_t *datalen); int loadFileEML_safe(const char *preferredName, void **pdata, size_t *datalen); +/** + * @brief Utility function to load data from a textfile (MCT). This method takes a preferred name. + * E.g. dumpdata-15.mct + * + * @param preferredName + * @param data The data array to store the loaded bytes from file + * @param datalen the number of bytes loaded from file + * @return 0 for ok, 1 for failz +*/ +int loadFileMCT_safe(const char *preferredName, void **pdata, size_t *datalen); + /** * @brief Utility function to load data from a JSON textfile. This method takes a preferred name. * E.g. dumpdata-15.json From fe31bd4064fe1d84352b5493da525203968f9281 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Sun, 23 Apr 2023 11:32:32 +0200 Subject: [PATCH 12/62] cleaning out memory unsafe functions for loading files. Also made the loading of files to honor the preference dump/trace settings. Experimental support, which will need more love. I have been using it for some months now but there are still other save/load file operations scattered in the PM3 client which will not benefit from this yet. They need to be adapted too --- CHANGELOG.md | 1 + client/src/fileutils.c | 287 ++++++++++++++++++++++----------------- client/src/fileutils.h | 18 +-- client/src/preferences.c | 11 +- 4 files changed, 172 insertions(+), 145 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cb3c3e059..ef2cf914c 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 the PM3 client to honor the preferences dump/trace paths. experimental support (@iceman1001) - Added the possibility to load `.MCT` dump files (@iceman1001) - Changed `lf t55xx dump --ns` - now supports `no save` of memory (@iceman1001) - Fixed the USB enumeration process (@wh201906) diff --git a/client/src/fileutils.c b/client/src/fileutils.c index dfc59b9fb..bb9c7635e 100644 --- a/client/src/fileutils.c +++ b/client/src/fileutils.c @@ -181,37 +181,55 @@ static size_t path_size(savePaths_t a) { } char *newfilenamemcopy(const char *preferredName, const char *suffix) { + return newfilenamemcopyEx(preferredName, suffix, spDefault); +} + +char *newfilenamemcopyEx(const char *preferredName, const char *suffix, savePaths_t e_save_path) { if (preferredName == NULL || suffix == NULL) { return NULL; } uint16_t p_namelen = strlen(preferredName); - if (str_endswith(preferredName, suffix)) + if (str_endswith(preferredName, suffix)) { p_namelen -= strlen(suffix); + } - // 10: room for filenum to ensure new filename - const size_t len = p_namelen + strlen(suffix) + 1 + 10; + int save_path_len = path_size(e_save_path); - int foobar = path_size(spDefault); - (void) foobar; + // 1: null terminator + // 16: room for filenum to ensure new filename + // save_path_len + strlen(PATHSEP): the user preference save paths + const size_t len = p_namelen + strlen(suffix) + 1 + 16 + save_path_len + strlen(PATHSEP); char *fileName = (char *) calloc(len, sizeof(uint8_t)); if (fileName == NULL) { return NULL; } + char *pfn = fileName; + + // user preference save paths + if (save_path_len) { + snprintf(pfn, save_path_len + strlen(PATHSEP) + 1, "%s%s", g_session.defaultPaths[e_save_path], PATHSEP); + pfn += save_path_len + strlen(PATHSEP); + } + int num = 1; - snprintf(fileName, len, "%.*s%s", p_namelen, preferredName, suffix); + // modify filename + snprintf(pfn, len, "%.*s%s", p_namelen, preferredName, suffix); + + // check complete path/filename if exists while (fileExists(fileName)) { - snprintf(fileName, len, "%.*s-%d%s", p_namelen, preferredName, num, suffix); + // modify filename + snprintf(pfn, len, "%.*s-%03d%s", p_namelen, preferredName, num, suffix); num++; } - PrintAndLogEx(INFO, "FILE PATH: %s", fileName); return fileName; } +// --------- SAVE FILES int saveFile(const char *preferredName, const char *suffix, const void *data, size_t datalen) { if (data == NULL || datalen == 0) { @@ -240,13 +258,14 @@ int saveFile(const char *preferredName, const char *suffix, const void *data, si return PM3_SUCCESS; } +// dump file int saveFileEML(const char *preferredName, uint8_t *data, size_t datalen, size_t blocksize) { if (data == NULL || datalen == 0) { return PM3_EINVARG; } - char *fileName = newfilenamemcopy(preferredName, ".eml"); + char *fileName = newfilenamemcopyEx(preferredName, ".eml", spDump); if (fileName == NULL) { return PM3_EMALLOC; } @@ -290,16 +309,19 @@ out: return retval; } +// dump file (normally, we also got preference file, etc) int saveFileJSON(const char *preferredName, JSONFileType ftype, uint8_t *data, size_t datalen, void (*callback)(json_t *)) { - return saveFileJSONex(preferredName, ftype, data, datalen, true, callback); + return saveFileJSONex(preferredName, ftype, data, datalen, true, callback, spDump); } -int saveFileJSONex(const char *preferredName, JSONFileType ftype, uint8_t *data, size_t datalen, bool verbose, void (*callback)(json_t *)) { +int saveFileJSONex(const char *preferredName, JSONFileType ftype, uint8_t *data, size_t datalen, bool verbose, void (*callback)(json_t *), savePaths_t e_save_path) { + if (ftype != jsfCustom) { if (data == NULL || datalen == 0) { return PM3_EINVARG; } + } - char *fileName = newfilenamemcopy(preferredName, ".json"); + char *fileName = newfilenamemcopyEx(preferredName, ".json", e_save_path); if (fileName == NULL) { return PM3_EMALLOC; } @@ -638,7 +660,7 @@ int saveFileJSONrootEx(const char *preferredName, void *root, size_t flags, bool if (overwrite) filename = filenamemcopy(preferredName, ".json"); else - filename = newfilenamemcopy(preferredName, ".json"); + filename = newfilenamemcopyEx(preferredName, ".json", spDump); if (filename == NULL) return PM3_EMALLOC; @@ -658,13 +680,14 @@ int saveFileJSONrootEx(const char *preferredName, void *root, size_t flags, bool return PM3_EFILE; } +// wave file of trace, int saveFileWAVE(const char *preferredName, const int *data, size_t datalen) { if (data == NULL || datalen == 0) { return PM3_EINVARG; } - char *fileName = newfilenamemcopy(preferredName, ".wav"); + char *fileName = newfilenamemcopyEx(preferredName, ".wav", spTrace); if (fileName == NULL) { return PM3_EMALLOC; } @@ -710,13 +733,14 @@ out: return retval; } +// Signal trace file, PM3 int saveFilePM3(const char *preferredName, int *data, size_t datalen) { if (data == NULL || datalen == 0) { return PM3_EINVARG; } - char *fileName = newfilenamemcopy(preferredName, ".pm3"); + char *fileName = newfilenamemcopyEx(preferredName, ".pm3", spTrace); if (fileName == NULL) { return PM3_EMALLOC; } @@ -743,11 +767,12 @@ out: return retval; } +// key file dump int createMfcKeyDump(const char *preferredName, uint8_t sectorsCnt, sector_t *e_sector) { if (e_sector == NULL) return PM3_EINVARG; - char *fileName = newfilenamemcopy(preferredName, ".bin"); + char *fileName = newfilenamemcopyEx(preferredName, ".bin", spDump); if (fileName == NULL) return PM3_EMALLOC; FILE *f = fopen(fileName, "wb"); @@ -785,66 +810,7 @@ int createMfcKeyDump(const char *preferredName, uint8_t sectorsCnt, sector_t *e_ return PM3_SUCCESS; } -int loadFile(const char *preferredName, const char *suffix, void *data, size_t maxdatalen, size_t *datalen) { - - if (data == NULL) return 1; - char *fileName = filenamemcopy(preferredName, suffix); - if (fileName == NULL) return PM3_EINVARG; - - int retval = PM3_SUCCESS; - - FILE *f = fopen(fileName, "rb"); - if (!f) { - PrintAndLogEx(WARNING, "file not found or locked. '" _YELLOW_("%s")"'", fileName); - free(fileName); - return PM3_EFILE; - } - - // get filesize in order to malloc memory - fseek(f, 0, SEEK_END); - long fsize = ftell(f); - fseek(f, 0, SEEK_SET); - - if (fsize <= 0) { - PrintAndLogEx(FAILED, "error, when getting filesize"); - retval = PM3_EFILE; - goto out; - } - - uint8_t *dump = calloc(fsize, sizeof(uint8_t)); - if (!dump) { - PrintAndLogEx(FAILED, "error, cannot allocate memory"); - retval = PM3_EMALLOC; - goto out; - } - - size_t bytes_read = fread(dump, 1, fsize, f); - - if (bytes_read != fsize) { - PrintAndLogEx(FAILED, "error, bytes read mismatch file size"); - free(dump); - retval = PM3_EFILE; - goto out; - } - - if (bytes_read > maxdatalen) { - PrintAndLogEx(WARNING, "Warning, bytes read exceed calling array limit. Max bytes is %zu bytes", maxdatalen); - bytes_read = maxdatalen; - } - - memcpy((data), dump, bytes_read); - free(dump); - - PrintAndLogEx(SUCCESS, "loaded " _YELLOW_("%zu") " bytes from binary file " _YELLOW_("%s"), bytes_read, fileName); - - *datalen = bytes_read; - -out: - fclose(f); - free(fileName); - return retval; -} - +// --------- LOAD FILES int loadFile_safe(const char *preferredName, const char *suffix, void **pdata, size_t *datalen) { return loadFile_safeEx(preferredName, suffix, pdata, datalen, true); } @@ -894,34 +860,53 @@ int loadFile_safeEx(const char *preferredName, const char *suffix, void **pdata, *datalen = bytes_read; - if (verbose) + if (verbose) { PrintAndLogEx(SUCCESS, "loaded " _YELLOW_("%zu") " bytes from binary file " _YELLOW_("%s"), bytes_read, preferredName); + } return PM3_SUCCESS; } -int loadFileEML(const char *preferredName, void *data, size_t *datalen) { +int loadFileEML_safe(const char *preferredName, void **pdata, size_t *datalen) { + char *path; + int res = searchFile(&path, RESOURCES_SUBDIR, preferredName, "", false); + if (res != PM3_SUCCESS) { + return PM3_EFILE; + } - if (data == NULL) return PM3_EINVARG; - - char *fileName = filenamemcopy(preferredName, ".eml"); - if (fileName == NULL) return PM3_EMALLOC; - - size_t counter = 0; - int retval = PM3_SUCCESS, hexlen = 0; - - FILE *f = fopen(fileName, "r"); + FILE *f = fopen(path, "r"); if (!f) { - PrintAndLogEx(WARNING, "file not found or locked. '" _YELLOW_("%s")"'", fileName); - retval = PM3_EFILE; - goto out; + PrintAndLogEx(WARNING, "file not found or locked `" _YELLOW_("%s") "`", path); + free(path); + return PM3_EFILE; + } + free(path); + + // get filesize in order to malloc memory + fseek(f, 0, SEEK_END); + long fsize = ftell(f); + fseek(f, 0, SEEK_SET); + + if (fsize <= 0) { + PrintAndLogEx(FAILED, "error, when getting filesize"); + fclose(f); + return PM3_EFILE; + } + + *pdata = calloc(fsize, sizeof(uint8_t)); + if (!*pdata) { + PrintAndLogEx(FAILED, "error, cannot allocate memory"); + fclose(f); + return PM3_EMALLOC; } // 128 + 2 newline chars + 1 null terminator char line[131]; memset(line, 0, sizeof(line)); uint8_t buf[64] = {0x00}; + size_t counter = 0; + int retval = PM3_SUCCESS, hexlen = 0; - uint8_t *udata = (uint8_t *)data; + uint8_t *tmp = (uint8_t *)*pdata; while (!feof(f)) { @@ -933,8 +918,7 @@ int loadFileEML(const char *preferredName, void *data, size_t *datalen) { fclose(f); PrintAndLogEx(FAILED, "File reading error."); - retval = PM3_EFILE; - goto out; + return PM3_EFILE; } if (line[0] == '#') @@ -942,22 +926,29 @@ int loadFileEML(const char *preferredName, void *data, size_t *datalen) { strcleanrn(line, sizeof(line)); - int res = param_gethex_to_eol(line, 0, buf, sizeof(buf), &hexlen); + res = param_gethex_to_eol(line, 0, buf, sizeof(buf), &hexlen); if (res == 0) { - memcpy(udata + counter, buf, hexlen); + memcpy(tmp + counter, buf, hexlen); counter += hexlen; } else { retval = PM3_ESOFT; } } fclose(f); - PrintAndLogEx(SUCCESS, "loaded " _YELLOW_("%zu") " bytes from text file " _YELLOW_("%s"), counter, fileName); + PrintAndLogEx(SUCCESS, "loaded " _YELLOW_("%zu") " bytes from text file " _YELLOW_("%s"), counter, preferredName); + + + uint8_t *newdump = realloc(*pdata, counter); + if (newdump == NULL) { + free(*pdata); + return PM3_EMALLOC; + } else { + *pdata = newdump; + } if (datalen) *datalen = counter; -out: - free(fileName); return retval; } @@ -1824,51 +1815,84 @@ int searchAndList(const char *pm3dir, const char *ext) { } static int searchFinalFile(char **foundpath, const char *pm3dir, const char *searchname, bool silent) { - if ((foundpath == NULL) || (pm3dir == NULL) || (searchname == NULL)) return PM3_ESOFT; + + if ((foundpath == NULL) || (pm3dir == NULL) || (searchname == NULL)) { + return PM3_ESOFT; + } + // explicit absolute (/) or relative path (./) => try only to match it directly char *filename = calloc(strlen(searchname) + 1, sizeof(char)); - if (filename == NULL) return PM3_EMALLOC; + if (filename == NULL) { + return PM3_EMALLOC; + } + strcpy(filename, searchname); if ((g_debugMode == 2) && (!silent)) { - PrintAndLogEx(INFO, "Searching %s", filename); + PrintAndLogEx(INFO, "pm3dir...... %s", pm3dir); + PrintAndLogEx(INFO, "Searching... %s", filename); } + + // try implicit relative path + PrintAndLogEx(DEBUG, "Searching implicit relative paths"); + if (fileExists(filename)) { + *foundpath = filename; + if ((g_debugMode == 2) && (!silent)) { + PrintAndLogEx(INFO, "Found %s", *foundpath); + } + return PM3_SUCCESS; + } + if (((strlen(filename) > 1) && (filename[0] == '/')) || ((strlen(filename) > 2) && (filename[0] == '.') && (filename[1] == '/'))) { - if (fileExists(filename)) { - *foundpath = filename; + goto out; + } + + // try the session paths + PrintAndLogEx(DEBUG, "Searching preferences paths"); + for (int i = 0; i < spItemCount; i++) { + + size_t sn = strlen(g_session.defaultPaths[i]) + strlen(filename) + strlen(PATHSEP) + 1; + char *default_path = calloc(sn, sizeof(char)); + if (default_path == NULL) { + goto out; + } + + snprintf(default_path, sn, "%s%s%s", g_session.defaultPaths[i], PATHSEP, filename); + + if ((g_debugMode == 2) && (!silent)) { + PrintAndLogEx(INFO, "Searching %s", default_path); + } + + if (fileExists(default_path)) { + free(filename); + *foundpath = default_path; if ((g_debugMode == 2) && (!silent)) { PrintAndLogEx(INFO, "Found %s", *foundpath); } return PM3_SUCCESS; } else { - goto out; + free(default_path); } } - // else - // try implicit relative path - { - if (fileExists(filename)) { - *foundpath = filename; - if ((g_debugMode == 2) && (!silent)) { - PrintAndLogEx(INFO, "Found %s", *foundpath); - } - return PM3_SUCCESS; - } - } // try pm3 dirs in user .proxmark3 (user mode) + PrintAndLogEx(DEBUG, "Searching user .proxmark3 paths"); const char *user_path = get_my_user_directory(); if (user_path != NULL) { char *path = calloc(strlen(user_path) + strlen(PM3_USER_DIRECTORY) + strlen(pm3dir) + strlen(filename) + 1, sizeof(char)); - if (path == NULL) + if (path == NULL) { goto out; + } + strcpy(path, user_path); strcat(path, PM3_USER_DIRECTORY); strcat(path, pm3dir); strcat(path, filename); + if ((g_debugMode == 2) && (!silent)) { PrintAndLogEx(INFO, "Searching %s", path); } + if (fileExists(path)) { free(filename); *foundpath = path; @@ -1880,7 +1904,9 @@ static int searchFinalFile(char **foundpath, const char *pm3dir, const char *sea free(path); } } + // try pm3 dirs in current client workdir (dev mode) + PrintAndLogEx(DEBUG, "Searching current workdir paths"); const char *exec_path = get_my_executable_directory(); if ((exec_path != NULL) && ((strcmp(DICTIONARIES_SUBDIR, pm3dir) == 0) || @@ -1913,23 +1939,28 @@ static int searchFinalFile(char **foundpath, const char *pm3dir, const char *sea free(path); } } + // try pm3 dirs in current repo workdir (dev mode) + PrintAndLogEx(DEBUG, "Searching PM3 dirs in current workdir"); if ((exec_path != NULL) && ((strcmp(TRACES_SUBDIR, pm3dir) == 0) || (strcmp(FIRMWARES_SUBDIR, pm3dir) == 0) || (strcmp(BOOTROM_SUBDIR, pm3dir) == 0) || (strcmp(FULLIMAGE_SUBDIR, pm3dir) == 0))) { - const char *above = "../"; - char *path = calloc(strlen(exec_path) + strlen(above) + strlen(pm3dir) + strlen(filename) + 1, sizeof(char)); - if (path == NULL) + char *path = calloc(strlen(exec_path) + strlen(ABOVE) + strlen(pm3dir) + strlen(filename) + 1, sizeof(char)); + if (path == NULL) { goto out; + } + strcpy(path, exec_path); - strcat(path, above); + strcat(path, ABOVE); strcat(path, pm3dir); strcat(path, filename); + if ((g_debugMode == 2) && (!silent)) { PrintAndLogEx(INFO, "Searching %s", path); } + if (fileExists(path)) { free(filename); *foundpath = path; @@ -1941,18 +1972,24 @@ static int searchFinalFile(char **foundpath, const char *pm3dir, const char *sea free(path); } } + // try pm3 dirs in pm3 installation dir (install mode) + PrintAndLogEx(DEBUG, "Searching PM3 installation dir paths"); if (exec_path != NULL) { char *path = calloc(strlen(exec_path) + strlen(PM3_SHARE_RELPATH) + strlen(pm3dir) + strlen(filename) + 1, sizeof(char)); - if (path == NULL) + if (path == NULL) { goto out; + } + strcpy(path, exec_path); strcat(path, PM3_SHARE_RELPATH); strcat(path, pm3dir); strcat(path, filename); + if ((g_debugMode == 2) && (!silent)) { PrintAndLogEx(INFO, "Searching %s", path); } + if (fileExists(path)) { free(filename); *foundpath = path; @@ -1988,15 +2025,15 @@ int searchFile(char **foundpath, const char *pm3dir, const char *searchname, con free(filename); return PM3_EFILE; } + int res = searchFinalFile(foundpath, pm3dir, filename, silent); if (res != PM3_SUCCESS) { - if ((res == PM3_EFILE) && (!silent)) + if ((res == PM3_EFILE) && (!silent)) { PrintAndLogEx(FAILED, "Error - can't find `" _YELLOW_("%s") "`", filename); + } + } free(filename); return res; - } - free(filename); - return PM3_SUCCESS; } int pm3_load_dump(const char *fn, void **pdump, size_t *dumplen, size_t maxdumplen) { diff --git a/client/src/fileutils.h b/client/src/fileutils.h index 5a49f529a..81cb13e05 100644 --- a/client/src/fileutils.h +++ b/client/src/fileutils.h @@ -60,12 +60,12 @@ typedef enum { } DumpFileType_t; int fileExists(const char *filename); -//bool create_path(const char *dirname); // set a path in the path list g_session.defaultPaths bool setDefaultPath(savePaths_t pathIndex, const char *path); char *newfilenamemcopy(const char *preferredName, const char *suffix); +char *newfilenamemcopyEx(const char *preferredName, const char *suffix, savePaths_t save_path); /** * @brief Utility function to save data to a binary file. This method takes a preferred name, but if that @@ -105,7 +105,7 @@ int saveFileEML(const char *preferredName, uint8_t *data, size_t datalen, size_t * @return 0 for ok, 1 for failz */ int saveFileJSON(const char *preferredName, JSONFileType ftype, uint8_t *data, size_t datalen, void (*callback)(json_t *)); -int saveFileJSONex(const char *preferredName, JSONFileType ftype, uint8_t *data, size_t datalen, bool verbose, void (*callback)(json_t *)); +int saveFileJSONex(const char *preferredName, JSONFileType ftype, uint8_t *data, size_t datalen, bool verbose, void (*callback)(json_t *), savePaths_t e_save_path); int saveFileJSONroot(const char *preferredName, void *root, size_t flags, bool verbose); int saveFileJSONrootEx(const char *preferredName, void *root, size_t flags, bool verbose, bool overwrite); /** STUB @@ -142,20 +142,6 @@ int saveFilePM3(const char *preferredName, int *data, size_t datalen); */ int createMfcKeyDump(const char *preferredName, uint8_t sectorsCnt, sector_t *e_sector); -/** - * @brief Utility function to load data from a binary file. This method takes a preferred name. - * E.g. dumpdata-15.bin - * - * @param preferredName - * @param suffix the file suffix. Including the ".". - * @param data The data array to store the loaded bytes from file - * @param maxdatalen the number of bytes that your data array has - * @param datalen the number of bytes loaded from file - * @return PM3_SUCCESS for ok, PM3_E* for failz -*/ -int loadFile(const char *preferredName, const char *suffix, void *data, size_t maxdatalen, size_t *datalen); - - /** * @brief Utility function to load data from a binary file. This method takes a preferred name. * E.g. dumpdata-15.bin, tries to search for it, and allocated memory. diff --git a/client/src/preferences.c b/client/src/preferences.c index 99fb20d30..e3aef4ccb 100644 --- a/client/src/preferences.c +++ b/client/src/preferences.c @@ -122,17 +122,18 @@ int preferences_save(void) { PrintAndLogEx(INFO, "Saving preferences..."); char *fn = prefGetFilename(); - int fnLen = strlen(fn) + 5; // .bak\0 + int fn_len = strlen(fn) + 5; // .bak\0 // [FILENAME_MAX+sizeof(preferencesFilename)+10] - char *backupFilename = (char *)calloc(fnLen, sizeof(uint8_t)); + char *backupFilename = (char *)calloc(fn_len, sizeof(uint8_t)); if (backupFilename == NULL) { PrintAndLogEx(ERR, "failed to allocate memory"); free(fn); return PM3_EMALLOC; } - snprintf(backupFilename, fnLen, "%s.bak", fn); + snprintf(backupFilename, fn_len, "%s.bak", fn); + // remove old backup file if (fileExists(backupFilename)) { if (remove(backupFilename) != 0) { PrintAndLogEx(FAILED, "Error - could not delete old settings backup file \"%s\"", backupFilename); @@ -142,6 +143,7 @@ int preferences_save(void) { } } + // rename file to backup file if (fileExists(fn)) { if (rename(fn, backupFilename) != 0) { PrintAndLogEx(FAILED, "Error - could not backup settings file \"%s\" to \"%s\"", fn, backupFilename); @@ -154,8 +156,9 @@ int preferences_save(void) { uint8_t dummyData = 0x00; size_t dummyDL = 0x01; - if (saveFileJSON(fn, jsfCustom, &dummyData, dummyDL, &preferences_save_callback) != PM3_SUCCESS) + if (saveFileJSONex(fn, jsfCustom, &dummyData, dummyDL, true, &preferences_save_callback, spItemCount) != PM3_SUCCESS) { PrintAndLogEx(ERR, "Error saving preferences to \"%s\"", fn); + } free(fn); free(backupFilename); From e252668866df6db7e106f3cc5bba0089c23e0667 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Sun, 23 Apr 2023 11:32:59 +0200 Subject: [PATCH 13/62] style --- client/src/flash.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/client/src/flash.c b/client/src/flash.c index 20729b2da..cca2de7b1 100644 --- a/client/src/flash.c +++ b/client/src/flash.c @@ -236,11 +236,14 @@ static int check_segs(flash_file_t *ctx, int can_write_bl, uint32_t flash_size) } static int print_and_validate_version(struct version_information_t *vi) { - if (vi->magic != VERSION_INFORMATION_MAGIC) + if (vi->magic != VERSION_INFORMATION_MAGIC) { return PM3_EFILE; + } + char temp[PM3_CMD_DATA_SIZE - 12]; // same limit as for ARM image FormatVersionInformation(temp, sizeof(temp), "", vi); PrintAndLogEx(SUCCESS, _CYAN_("ELF file version") _YELLOW_(" %s"), temp); + if (strlen(g_version_information.armsrc) == 9) { if (strncmp(vi->armsrc, g_version_information.armsrc, 9) != 0) { PrintAndLogEx(WARNING, _RED_("ARM firmware does not match the source at the time the client was compiled")); From 6fe32635760bc17caaf89e5ef115e4ebec35f98b Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Wed, 26 Apr 2023 00:06:55 +0200 Subject: [PATCH 14/62] hf mfu dump now supports the --ns param to not save the memory dump to file --- CHANGELOG.md | 1 + client/src/cmdhfmfu.c | 31 ++++++++++++++++++------------- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ef2cf914c..c4d075061 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 mfu dump --ns` - now supports `no save` of card memory (@iceman1001) - Changed the PM3 client to honor the preferences dump/trace paths. experimental support (@iceman1001) - Added the possibility to load `.MCT` dump files (@iceman1001) - Changed `lf t55xx dump --ns` - now supports `no save` of memory (@iceman1001) diff --git a/client/src/cmdhfmfu.c b/client/src/cmdhfmfu.c index 963786ebc..e47a60e7c 100644 --- a/client/src/cmdhfmfu.c +++ b/client/src/cmdhfmfu.c @@ -2439,6 +2439,7 @@ static int CmdHF14AMfUDump(const char *Cmd) { arg_lit0("l", NULL, "Swap entered key's endianness"), arg_int0("p", "page", "", "Manually set start page number to start from"), arg_int0("q", "qty", "", "Manually set number of pages to dump"), + arg_lit0(NULL, "ns", "no save to file"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -2454,6 +2455,7 @@ static int CmdHF14AMfUDump(const char *Cmd) { bool swap_endian = arg_get_lit(ctx, 3); int start_page = arg_get_int_def(ctx, 4, 0); int pages = arg_get_int_def(ctx, 5, 16); + bool nosave = arg_get_lit(ctx, 6); CLIParserFree(ctx); bool has_auth_key = false; @@ -2649,21 +2651,24 @@ static int CmdHF14AMfUDump(const char *Cmd) { printMFUdumpEx(&dump_file_data, pages, start_page); - // user supplied filename? - if (fnlen < 1) { + if (nosave == false) { + // user supplied filename? + if (fnlen < 1) { + PrintAndLogEx(INFO, "Using UID as filename"); + uint8_t uid[7] = {0}; + memcpy(uid, (uint8_t *)&dump_file_data.data, 3); + memcpy(uid + 3, (uint8_t *)&dump_file_data.data + 4, 4); + strcat(filename, "hf-mfu-"); + FillFileNameByUID(filename, uid, "-dump", sizeof(uid)); + } - PrintAndLogEx(INFO, "Using UID as filename"); - uint8_t uid[7] = {0}; - memcpy(uid, (uint8_t *)&dump_file_data.data, 3); - memcpy(uid + 3, (uint8_t *)&dump_file_data.data + 4, 4); - strcat(filename, "hf-mfu-"); - FillFileNameByUID(filename, uid, "-dump", sizeof(uid)); + uint16_t datalen = pages * MFU_BLOCK_SIZE + MFU_DUMP_PREFIX_LENGTH; + pm3_save_dump(filename, (uint8_t *)&dump_file_data, datalen, jsfMfuMemory, MFU_BLOCK_SIZE); + + if (is_partial) { + PrintAndLogEx(WARNING, "Partial dump created. (%d of %d blocks)", pages, card_mem_size); + } } - uint16_t datalen = pages * MFU_BLOCK_SIZE + MFU_DUMP_PREFIX_LENGTH; - pm3_save_dump(filename, (uint8_t *)&dump_file_data, datalen, jsfMfuMemory, MFU_BLOCK_SIZE); - - if (is_partial) - PrintAndLogEx(WARNING, "Partial dump created. (%d of %d blocks)", pages, card_mem_size); return PM3_SUCCESS; } From 94b0bcc7580cc4523ba98e5d646a688f7d735345 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Wed, 26 Apr 2023 00:08:01 +0200 Subject: [PATCH 15/62] changed the key table output. In some cases it didnt print non found keys red --- client/src/cmdhfmf.c | 90 ++++++++++++++++++++++---------------------- 1 file changed, 44 insertions(+), 46 deletions(-) diff --git a/client/src/cmdhfmf.c b/client/src/cmdhfmf.c index 765342ec2..a647d90c2 100644 --- a/client/src/cmdhfmf.c +++ b/client/src/cmdhfmf.c @@ -2861,6 +2861,9 @@ tryNested: } case PM3_ESTATIC_NONCE: PrintAndLogEx(ERR, "Error: Static encrypted nonce detected. Aborted\n"); + + e_sector[current_sector_i].Key[current_key_type_i] = 0xffffffffffff;; + e_sector[current_sector_i].foundKey[current_key_type_i] = false; // Show the results to the user PrintAndLogEx(NORMAL, ""); PrintAndLogEx(SUCCESS, _GREEN_("found keys:")); @@ -2907,6 +2910,10 @@ tryHardnested: // If the nested attack fails then we try the hardnested attack } case PM3_ESTATIC_NONCE: { PrintAndLogEx(ERR, "\nError: Static encrypted nonce detected. Aborted\n"); + + e_sector[current_sector_i].Key[current_key_type_i] = 0xffffffffffff;; + e_sector[current_sector_i].foundKey[current_key_type_i] = false; + // Show the results to the user PrintAndLogEx(NORMAL, ""); PrintAndLogEx(SUCCESS, _GREEN_("found keys:")); @@ -3899,57 +3906,48 @@ void printKeyTableEx(uint8_t sectorscnt, sector_t *e_sector, uint8_t start_secto PrintAndLogEx(SUCCESS, "-----+-----+--------------+---+--------------+----"); PrintAndLogEx(SUCCESS, " Sec | Blk | key A |res| key B |res"); PrintAndLogEx(SUCCESS, "-----+-----+--------------+---+--------------+----"); + + bool extended_legend = false; for (uint8_t i = 0; i < sectorscnt; i++) { - snprintf(strA, sizeof(strA), "------------"); - snprintf(strB, sizeof(strB), "------------"); - - if (e_sector[i].foundKey[0]) - snprintf(strA, sizeof(strA), "%012" PRIX64, e_sector[i].Key[0]); - - if (e_sector[i].foundKey[1]) - snprintf(strB, sizeof(strB), "%012" PRIX64, e_sector[i].Key[1]); - - if (e_sector[i].foundKey[0] > 1) { - PrintAndLogEx(SUCCESS, " "_YELLOW_("%03d")" | %03d | " _GREEN_("%s")" | " _BRIGHT_GREEN_("%c")" | " _GREEN_("%s")" | " _BRIGHT_GREEN_("%c") - , i - , mfSectorTrailerOfSector(i) - , strA, e_sector[i].foundKey[0] - , strB, e_sector[i].foundKey[1] - ); - } else { - - // keep track if we use start_sector or i... - uint8_t s = start_sector; - if (start_sector == 0) - s = i; - - if (e_sector[i].foundKey[0]) { - snprintf(strA, sizeof(strA), _GREEN_("%012" PRIX64), e_sector[i].Key[0]); - snprintf(resA, sizeof(resA), _BRIGHT_GREEN_("%d"), 1); - } else { - snprintf(strA, sizeof(strA), _RED_("%s"), "------------"); - snprintf(resA, sizeof(resA), _RED_("%d"), 0); - } - - if (e_sector[i].foundKey[1]) { - snprintf(strB, sizeof(strB), _GREEN_("%012" PRIX64), e_sector[i].Key[1]); - snprintf(resB, sizeof(resB), _BRIGHT_GREEN_("%d"), 1); - } else { - snprintf(strB, sizeof(strB), _RED_("%s"), "------------"); - snprintf(resB, sizeof(resB), _RED_("%d"), 0); - } - - PrintAndLogEx(SUCCESS, " " _YELLOW_("%03d") " | %03d | %s | %s | %s | %s" - , s - , mfSectorTrailerOfSector(s) - , strA, resA - , strB, resB - ); + if ((e_sector[i].foundKey[0] > 1) || (e_sector[i].foundKey[1] > 1)) { + extended_legend = true; } + + if (e_sector[i].foundKey[0]) { + snprintf(strA, sizeof(strA), _GREEN_("%012" PRIX64), e_sector[i].Key[0]); + snprintf(resA, sizeof(resA), _BRIGHT_GREEN_("%c"), e_sector[i].foundKey[0]); + } else { + snprintf(strA, sizeof(strA), _RED_("%s"), "------------"); + snprintf(resA, sizeof(resA), _RED_("%d"), 0); + } + + if (e_sector[i].foundKey[1]) { + snprintf(strB, sizeof(strB), _GREEN_("%012" PRIX64), e_sector[i].Key[1]); + snprintf(resB, sizeof(resB), _BRIGHT_GREEN_("%c"), e_sector[i].foundKey[1]); + } else { + snprintf(strB, sizeof(strB), _RED_("%s"), "------------"); + snprintf(resB, sizeof(resB), _RED_("%d"), 0); + } + + // keep track if we use start_sector or i + // show one sector or all. + uint8_t s = start_sector; + if (start_sector == 0) { + s = i; + } + + PrintAndLogEx(SUCCESS, " " _YELLOW_("%03d") " | %03d | %s | %s | %s | %s" + , s + , mfSectorTrailerOfSector(s) + , strA, resA + , strB, resB + ); } + PrintAndLogEx(SUCCESS, "-----+-----+--------------+---+--------------+----"); - if (e_sector[0].foundKey[0] > 1) { + + if (extended_legend) { PrintAndLogEx(INFO, "( " _YELLOW_("D") ":Dictionary / " _YELLOW_("S") ":darkSide / " From d480cbd1d0f679cf009887e6d533ab469a1b71e3 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Wed, 26 Apr 2023 00:10:15 +0200 Subject: [PATCH 16/62] hf 14b dump now supports the no save parameter --- CHANGELOG.md | 1 + client/src/cmdhf14b.c | 20 ++++++++++++-------- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c4d075061..c73f7641a 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 14b dump --ns` - now supports `no save` of card memory (@iceman1001) - Changed `hf mfu dump --ns` - now supports `no save` of card memory (@iceman1001) - Changed the PM3 client to honor the preferences dump/trace paths. experimental support (@iceman1001) - Added the possibility to load `.MCT` dump files (@iceman1001) diff --git a/client/src/cmdhf14b.c b/client/src/cmdhf14b.c index 12440c676..d0083fbb5 100644 --- a/client/src/cmdhf14b.c +++ b/client/src/cmdhf14b.c @@ -1359,6 +1359,7 @@ static int CmdHF14BDump(const char *Cmd) { void *argtable[] = { arg_param_begin, arg_str0("f", "file", "", "(optional) filename, if no UID will be used as filename"), + arg_lit0(NULL, "ns", "no save to file"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -1366,6 +1367,7 @@ static int CmdHF14BDump(const char *Cmd) { int fnlen = 0; char filename[FILE_PATH_SIZE] = {0}; CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); + bool nosave = arg_get_lit(ctx, 2); CLIParserFree(ctx); @@ -1514,15 +1516,17 @@ static int CmdHF14BDump(const char *Cmd) { print_sr_blocks(data, cardsize, card.uid); - // save to file - if (fnlen < 1) { - PrintAndLogEx(INFO, "using UID as filename"); - char *fptr = filename + snprintf(filename, sizeof(filename), "hf-14b-"); - FillFileNameByUID(fptr, SwapEndian64(card.uid, card.uidlen, 8), "-dump", card.uidlen); - } + if (nosave == false) { + // save to file + if (fnlen < 1) { + PrintAndLogEx(INFO, "using UID as filename"); + char *fptr = filename + snprintf(filename, sizeof(filename), "hf-14b-"); + FillFileNameByUID(fptr, SwapEndian64(card.uid, card.uidlen, 8), "-dump", card.uidlen); + } - size_t datalen = (lastblock + 2) * ST25TB_SR_BLOCK_SIZE; - pm3_save_dump(filename, data, datalen, jsf14b, ST25TB_SR_BLOCK_SIZE); + size_t datalen = (lastblock + 2) * ST25TB_SR_BLOCK_SIZE; + pm3_save_dump(filename, data, datalen, jsf14b, ST25TB_SR_BLOCK_SIZE); + } } return switch_off_field_14b(); From 9eca7fce8394aad879993134aaf41a259e39ac64 Mon Sep 17 00:00:00 2001 From: Mistial Developer Date: Fri, 28 Apr 2023 00:09:24 -0500 Subject: [PATCH 17/62] Add text explaining that the format is H10301 for binary, and specifying it for non-binary --- client/src/cmdhficlass.c | 6 +++--- doc/commands.json | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index b328436bb..ba6d984ad 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -3836,9 +3836,9 @@ static int CmdHFiClassEncode(const char *Cmd) { CLIParserInit(&ctx, "hf iclass encode", "Encode binary wiegand to block 7,8,9\n" "Use either --bin or --wiegand/--fc/--cn", - "hf iclass encode --bin 10001111100000001010100011 --ki 0 -> FC 31 CN 337\n" - "hf iclass encode --fc 31 --cn 337 --ki 0 -> FC 31 CN 337\n" - "hf iclass encode --bin 10001111100000001010100011 --ki 0 --elite -> FC 31 CN 337, writing w elite key" + "hf iclass encode --bin 10001111100000001010100011 --ki 0 -> FC 31 CN 337 (H10301)\n" + "hf iclass encode -w H10301 --fc 31 --cn 337 --ki 0 -> FC 31 CN 337 (H10301)\n" + "hf iclass encode --bin 10001111100000001010100011 --ki 0 --elite -> FC 31 CN 337 (H10301), writing w elite key" ); void *argtable[] = { diff --git a/doc/commands.json b/doc/commands.json index 703ff688c..f8712fc47 100644 --- a/doc/commands.json +++ b/doc/commands.json @@ -3100,9 +3100,9 @@ "command": "hf iclass encode", "description": "Encode binary wiegand to block 7,8,9 Use either --bin or --wiegand/--fc/--cn", "notes": [ - "hf iclass encode --bin 10001111100000001010100011 --ki 0 -> FC 31 CN 337", - "hf iclass encode --fc 31 --cn 337 --ki 0 -> FC 31 CN 337", - "hf iclass encode --bin 10001111100000001010100011 --ki 0 --elite -> FC 31 CN 337, writing w elite key" + "hf iclass encode --bin 10001111100000001010100011 --ki 0 -> FC 31 CN 337 (H10301)", + "hf iclass encode -w H10301 --fc 31 --cn 337 --ki 0 -> FC 31 CN 337 (H10301)", + "hf iclass encode --bin 10001111100000001010100011 --ki 0 --elite -> FC 31 CN 337 (H10301), writing w elite key" ], "offline": true, "options": [ @@ -11991,4 +11991,4 @@ "extracted_by": "PM3Help2JSON v1.00", "extracted_on": "2023-03-26T15:04:49" } -} \ No newline at end of file +} From 2678c8e1a45678b9236d650321b49537616a20c0 Mon Sep 17 00:00:00 2001 From: kormax Date: Tue, 2 May 2023 22:04:40 +0300 Subject: [PATCH 18/62] Add more AID entries --- client/resources/aidlist.json | 60 +++++++++++++++++++++++++++++++++-- 1 file changed, 58 insertions(+), 2 deletions(-) diff --git a/client/resources/aidlist.json b/client/resources/aidlist.json index 81a153bba..bd6e4eac8 100644 --- a/client/resources/aidlist.json +++ b/client/resources/aidlist.json @@ -2243,8 +2243,8 @@ "AID": "A0000002480400", "Vendor": "ISO/IEC JTC1/SC17", "Country": "", - "Name": "Personal identification (mDL)", - "Description": "ISO/IEC 18013-5:2021 compliant Mobile driving licence (mDL) application.", + "Name": "Personal identification (mDL) data transfer", + "Description": "ISO/IEC 18013-5:2021 compliant Mobile driving licence (mDL) application data transfer.", "Type": "identity" }, { @@ -2259,10 +2259,26 @@ "AID": "A000000809434343444B467631", "Vendor": "Car Connectivity Consortium (CCC)", "Country": "", + "Name": "Digital Car Key Framework", + "Description": "Used during key provisioning and configuration", + "Type": "access" + }, + { + "AID": "A000000809434343444B417631", + "Vendor": "Car Connectivity Consortium (CCC)", + "Country": "", "Name": "Digital Car Key", "Description": "", "Type": "access" }, + { + "AID": "A0000008580102", + "Vendor": "Apple", + "Country": "", + "Name": "Apple Home Key Framework", + "Description": "Home Key configuration applet. Selected after a first transaction on a newely-invited device (allegedly for mailbox sync/attestation exchange)", + "Type": "" + }, { "AID": "A0000008580101", "Vendor": "Apple", @@ -2270,5 +2286,45 @@ "Name": "Apple Home Key", "Description": "NFC Home Key for select HomeKit-compatible locks", "Type": "access" + }, + { + "AID": "A000000396564341", + "Vendor": "NXP", + "Country": "", + "Name": "MIFARE 2GO", + "Description": "RID used by MIFARE 2GO-based cards", + "Type": "" + }, + { + "AID": "A0000002164954534F2D31", + "Vendor": "ITSO", + "Country": "United Kingdom", + "Name": "ITSO CMD2", + "Description": "AID used by ITSO for smartcard/phone-based transit cards", + "Type": "transit" + }, + { + "AID": "A000000632010105", + "Vendor": "CTTIC", + "Country": "China", + "Name": "China T-Union", + "Description": "Universal transit card used by many big public transit operators", + "Type": "transit" + }, + { + "AID": "D2760000254D010200", + "Vendor": "Zentraler Kreditausschuss (ZKA)", + "Country": "Germany", + "Name": "Girocard Jugendschutz", + "Description": "Age verification", + "Type": "" + }, + { + "AID": "A00000000491", + "Vendor": "MasterCard International", + "Country": "", + "Name": "Mastercard Private Label Transit", + "Description": "RID Used by transit cards that use private label mastercards (E.g. Ventra and HOP)", + "Type": "transit" } ] From f9ea12b98d376a1c0f9aadb2f08a3ee9208cc693 Mon Sep 17 00:00:00 2001 From: Maksym Date: Wed, 3 May 2023 00:48:21 +0300 Subject: [PATCH 19/62] Update aidlist.json Replace "rid" with prefix as it is more correct this way --- client/resources/aidlist.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/resources/aidlist.json b/client/resources/aidlist.json index bd6e4eac8..ff574dd6a 100644 --- a/client/resources/aidlist.json +++ b/client/resources/aidlist.json @@ -2292,7 +2292,7 @@ "Vendor": "NXP", "Country": "", "Name": "MIFARE 2GO", - "Description": "RID used by MIFARE 2GO-based cards", + "Description": "AID prefix used by MIFARE 2GO-based cards", "Type": "" }, { @@ -2324,7 +2324,7 @@ "Vendor": "MasterCard International", "Country": "", "Name": "Mastercard Private Label Transit", - "Description": "RID Used by transit cards that use private label mastercards (E.g. Ventra and HOP)", + "Description": "AID prefix used by transit cards that use private label mastercards (E.g. Ventra and HOP)", "Type": "transit" } ] From f1aedc6bcedf3ae0c4f2c6570cab23e8b546e23f Mon Sep 17 00:00:00 2001 From: Nat McHugh Date: Tue, 2 May 2023 19:42:18 +0000 Subject: [PATCH 20/62] Add paxton id to hitg2 info --- CHANGELOG.md | 1 + client/src/cmdlfhitag.c | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c73f7641a..61333107d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -55,6 +55,7 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac - Changed `hf legic view` - now also print the decoded info of the dump file (@0xdeb) - Now `script run hf_mf_ultimatecard.lua -u` supports 10bytes UID (@alejandro12120) - Update documentation for installation on macOS with MacPorts (@linuxgemini) + - Added possible Paxton id to hitag2 tag info output ## [Nitride.4.16191][2023-01-29] - Changed `build_all_firmwares.sh` to fit GENERIC 256kb firmware images (@doegox) diff --git a/client/src/cmdlfhitag.c b/client/src/cmdlfhitag.c index 66fb3ea42..23b8ac235 100644 --- a/client/src/cmdlfhitag.c +++ b/client/src/cmdlfhitag.c @@ -324,6 +324,37 @@ static int CmdLFHitagSim(const char *Cmd) { return PM3_SUCCESS; } + +static void printHitag2PaxtonDowngrade(const uint8_t *data) { + + uint64_t bytes = 0; + uint64_t num = 0; + uint64_t paxton_id = 0; + uint16_t skip = 48; + uint16_t digit = 0; + uint64_t mask = 0xF80000000000; + + for (int i = 16; i < 22; i++) { + bytes = (bytes * 0x100) + data[i]; + } + + for (int j = 0; j< 8; j++) { + num = bytes & mask; + skip -= 5; + mask = mask >> 5; + digit = (num >> skip & 15); + paxton_id = (paxton_id * 10) + digit; + + if (j == 5) { + skip -= 2; + mask = mask >> 2; + } + } + + PrintAndLogEx(INFO, "-------- " _CYAN_("Possible de-scramble patterns") " ---------"); + PrintAndLogEx(SUCCESS, "Paxton id: %lu | 0x%lx", paxton_id, paxton_id); +} + static void printHitag2Configuration(uint8_t config) { char msg[100]; @@ -630,6 +661,8 @@ static int CmdLFHitagReader(const char *Cmd) { // print data print_hex_break(data, 48, 4); + + printHitag2PaxtonDowngrade(data); } return PM3_SUCCESS; } From b8c7e02ad847a7874ab04ca4246a6c0e2a70e9e4 Mon Sep 17 00:00:00 2001 From: flamebarke <39644720+flamebarke@users.noreply.github.com> Date: Thu, 4 May 2023 21:34:27 +1000 Subject: [PATCH 21/62] Create lf_multihid.c Signed-off-by: flamebarke <39644720+flamebarke@users.noreply.github.com> --- armsrc/Standalone/lf_multihid.c | 76 +++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 armsrc/Standalone/lf_multihid.c diff --git a/armsrc/Standalone/lf_multihid.c b/armsrc/Standalone/lf_multihid.c new file mode 100644 index 000000000..d2edd867c --- /dev/null +++ b/armsrc/Standalone/lf_multihid.c @@ -0,0 +1,76 @@ +//----------------------------------------------------------------------------- +// Copyright (C) Shain Lakin, 2023 +// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// See LICENSE.txt for the text of the license. +//----------------------------------------------------------------------------- +// LF HID 26 Bit (H10301) multi simulator: +// Simple LF HID26 (H10301) tag simulator +// Short click - select next slot and start simulation +// LEDS = LED ON for selected slot +// Add tags (raw) to the hid26_predefined_raw array +//----------------------------------------------------------------------------- + + +#include "standalone.h" +#include "proxmark3_arm.h" +#include "appmain.h" +#include "fpgaloader.h" +#include "util.h" +#include "dbprint.h" +#include "ticks.h" +#include "lfops.h" + +#define ARRAYLEN(x) (sizeof(x) / sizeof((x)[0])) +#define MAX_IND 4 + +void LED_Slot(int i); + +static uint64_t hid26_predefined_raw[] = {0x2004ec2e87, 0x2004421807, 0x20064312d6, 0x2006ec0c86}; +static uint8_t hid26_slots_count; + +void ModInfo(void) { + DbpString("LF HID 26 Bit (H10301) multi simulator - aka MultiHID (Shain Lakin)"); +} + +void LED_Slot(int i) { + LEDsoff(); + if (hid26_slots_count > 4) { + LED(i % MAX_IND, 0); + } else { + LED(1 << i, 0); + } +} + +void RunMod(void) { + StandAloneMode(); + FpgaDownloadAndGo(FPGA_BITSTREAM_LF); + Dbprintf(">> LF HID26 multi simulator started - aka MultiHID (Shain Lakin) <<"); + + int selected = 0; //selected slot after start + hid26_slots_count = ARRAYLEN(hid26_predefined_raw); + for (;;) { + WDT_HIT(); + if (data_available()) { + LEDsoff(); + break; + } + + SpinDelay(100); + SpinUp(100); + LED_Slot(selected); + uint64_t raw_data = hid26_predefined_raw[selected]; + CmdHIDsimTAG(0, raw_data >> 32, raw_data & 0xFFFFFFFF, 0, false); + selected = (selected + 1) % hid26_slots_count; + } +} From 0fac149a039d26a33e52ac685bd1a42ae48770e4 Mon Sep 17 00:00:00 2001 From: flamebarke <39644720+flamebarke@users.noreply.github.com> Date: Thu, 4 May 2023 21:44:12 +1000 Subject: [PATCH 22/62] Update Makefile.hal Signed-off-by: flamebarke <39644720+flamebarke@users.noreply.github.com> --- armsrc/Standalone/Makefile.hal | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/armsrc/Standalone/Makefile.hal b/armsrc/Standalone/Makefile.hal index cb41bc148..da204eaca 100644 --- a/armsrc/Standalone/Makefile.hal +++ b/armsrc/Standalone/Makefile.hal @@ -50,6 +50,9 @@ define KNOWN_STANDALONE_DEFINITIONS | LF_ICEHID | LF HID collector to flashmem | | (RDV4 only) | | +----------------------------------------------------------+ +| LF_MULTIHID | LF HID 26 Bit (H1031) multi simulator | +| | - Shain Lakin | ++----------------------------------------------------------+ | LF_NEDAP_SIM | LF Nedap ID simple simulator | | | | +----------------------------------------------------------+ @@ -126,7 +129,7 @@ endef STANDALONE_MODES := LF_SKELETON -STANDALONE_MODES += LF_EM4100EMUL LF_EM4100RSWB LF_EM4100RSWW LF_EM4100RWC LF_HIDBRUTE LF_HIDFCBRUTE LF_ICEHID LF_NEDAP_SIM LF_NEXID LF_PROXBRUTE LF_PROX2BRUTE LF_SAMYRUN LF_THAREXDE +STANDALONE_MODES += LF_EM4100EMUL LF_EM4100RSWB LF_EM4100RSWW LF_EM4100RWC LF_HIDBRUTE LF_HIDFCBRUTE LF_ICEHID LF_MULTIHID LF_NEDAP_SIM LF_NEXID LF_PROXBRUTE LF_PROX2BRUTE LF_SAMYRUN LF_THAREXDE STANDALONE_MODES += HF_14ASNIFF HF_14BSNIFF HF_15SNIFF HF_AVEFUL HF_BOG HF_COLIN HF_CRAFTBYTE HF_ICECLASS HF_LEGIC HF_LEGICSIM HF_MATTYRUN HF_MFCSIM HF_MSDSAL HF_REBLAY HF_TCPRST HF_TMUDFORD HF_YOUNG STANDALONE_MODES += DANKARMULTI STANDALONE_MODES_REQ_BT := HF_REBLAY From 92ec161d08380135d8a233ffe14f9b45ce648dc2 Mon Sep 17 00:00:00 2001 From: flamebarke <39644720+flamebarke@users.noreply.github.com> Date: Thu, 4 May 2023 21:50:14 +1000 Subject: [PATCH 23/62] Update Makefile.inc Signed-off-by: flamebarke <39644720+flamebarke@users.noreply.github.com> --- armsrc/Standalone/Makefile.inc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/armsrc/Standalone/Makefile.inc b/armsrc/Standalone/Makefile.inc index 48ac2217f..6aeb163bb 100644 --- a/armsrc/Standalone/Makefile.inc +++ b/armsrc/Standalone/Makefile.inc @@ -49,6 +49,10 @@ endif ifneq (,$(findstring WITH_STANDALONE_LF_ICEHID,$(APP_CFLAGS))) SRC_STANDALONE = lf_icehid.c endif +# WITH_STANDALONE_LF_MULTIHID +ifneq (,$(findstring WITH_STANDALONE_LF_MULTIHID,$(APP_CFLAGS))) + SRC_STANDALONE = lf_multihid.c +endif # WITH_STANDALONE_LF_NEDAP_SIM ifneq (,$(findstring WITH_STANDALONE_LF_NEDAP_SIM,$(APP_CFLAGS))) SRC_STANDALONE = lf_nedap_sim.c From 6880c51ad60bc760d298bc5c8884c3ebd530be88 Mon Sep 17 00:00:00 2001 From: flamebarke <39644720+flamebarke@users.noreply.github.com> Date: Thu, 4 May 2023 21:55:23 +1000 Subject: [PATCH 24/62] Update CHANGELOG.md Signed-off-by: flamebarke <39644720+flamebarke@users.noreply.github.com> --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 61333107d..d8c6d5e95 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 new standalone mode `LF_MULTIHID` - HID26 (H1031) multi simulator (@flamebarke) - Changed `hf 14b dump --ns` - now supports `no save` of card memory (@iceman1001) - Changed `hf mfu dump --ns` - now supports `no save` of card memory (@iceman1001) - Changed the PM3 client to honor the preferences dump/trace paths. experimental support (@iceman1001) From caf3bbc026696ff5aa6d20ded92b826e6e2f859d Mon Sep 17 00:00:00 2001 From: flamebarke <39644720+flamebarke@users.noreply.github.com> Date: Thu, 4 May 2023 22:26:39 +1000 Subject: [PATCH 25/62] Update 4_Advanced-compilation-parameters.md Signed-off-by: flamebarke <39644720+flamebarke@users.noreply.github.com> --- doc/md/Use_of_Proxmark/4_Advanced-compilation-parameters.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/md/Use_of_Proxmark/4_Advanced-compilation-parameters.md b/doc/md/Use_of_Proxmark/4_Advanced-compilation-parameters.md index 5ead60b1a..53726b9dd 100644 --- a/doc/md/Use_of_Proxmark/4_Advanced-compilation-parameters.md +++ b/doc/md/Use_of_Proxmark/4_Advanced-compilation-parameters.md @@ -108,6 +108,7 @@ Here are the supported values you can assign to `STANDALONE` in `Makefile.platfo | LF_HIDBRUTE | HID corporate 1000 bruteforce - Federico dotta & Maurizio Agazzini | LF_HIDFCBRUTE | LF HID facility code bruteforce - ss23 | LF_ICEHID | LF HID collector to flashmem - Iceman1001 +| LF_MULTIHID | LF HID 26 Bit (H1031) multi simulator - Shain Lakin | LF_NEDAP_SIM | LF Nedap ID simulator | LF_NEXID | Nexwatch credentials detection mode - jrjgjk & Zolorah | LF_PROXBRUTE | HID ProxII bruteforce - Brad Antoniewicz From dd28bc8e5d94f7740b77bc1824c606df951bca75 Mon Sep 17 00:00:00 2001 From: flamebarke <39644720+flamebarke@users.noreply.github.com> Date: Thu, 4 May 2023 22:29:14 +1000 Subject: [PATCH 26/62] Update build_all_firmwares.sh Signed-off-by: flamebarke <39644720+flamebarke@users.noreply.github.com> --- tools/build_all_firmwares.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/build_all_firmwares.sh b/tools/build_all_firmwares.sh index 644438032..b38f557ab 100755 --- a/tools/build_all_firmwares.sh +++ b/tools/build_all_firmwares.sh @@ -32,7 +32,7 @@ mv bootrom/obj/bootrom.elf "$DEST/PM3BOOTROM.elf" # cf armsrc/Standalone/Makefile.hal STANDALONE_MODES=(LF_SKELETON) -STANDALONE_MODES+=(LF_EM4100EMUL LF_EM4100RSWB LF_EM4100RSWW LF_EM4100RWC LF_HIDBRUTE LF_HIDFCBRUTE LF_ICEHID LF_NEDAP_SIM LF_NEXID LF_PROXBRUTE LF_PROX2BRUTE LF_SAMYRUN LF_THAREXDE) +STANDALONE_MODES+=(LF_EM4100EMUL LF_EM4100RSWB LF_EM4100RSWW LF_EM4100RWC LF_HIDBRUTE LF_HIDFCBRUTE LF_ICEHID LF_MULTIHID LF_NEDAP_SIM LF_NEXID LF_PROXBRUTE LF_PROX2BRUTE LF_SAMYRUN LF_THAREXDE) STANDALONE_MODES+=(HF_14ASNIFF HF_14BSNIFF HF_15SNIFF HF_AVEFUL HF_BOG HF_COLIN HF_CRAFTBYTE HF_ICECLASS HF_LEGIC HF_LEGICSIM HF_MATTYRUN HF_MFCSIM HF_MSDSAL HF_REBLAY HF_TCPRST HF_TMUDFORD HF_YOUNG) STANDALONE_MODES+=(DANKARMULTI) STANDALONE_MODES_REQ_BT=(HF_REBLAY) From b1839d7318f39f940b6925ec0e3f6ab83847c4ef Mon Sep 17 00:00:00 2001 From: Jonathan Liu Date: Fri, 5 May 2023 12:19:11 +1000 Subject: [PATCH 27/62] hf mf sim: reduce 50ms threshold to 6ms for reset to idle Fixes reader not being able to detect the simulated card on second Inventory command due to the RF field being powered off for only 6ms before being turned on again to reset the card to idle state. Closes #1974 --- CHANGELOG.md | 1 + armsrc/iso14443a.c | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 61333107d..2332cb2c0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -56,6 +56,7 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac - Now `script run hf_mf_ultimatecard.lua -u` supports 10bytes UID (@alejandro12120) - Update documentation for installation on macOS with MacPorts (@linuxgemini) - Added possible Paxton id to hitag2 tag info output + - Changed `hf mf sim` - reduce 50ms threshold to 6ms for reset to idle #1974 (@net147) ## [Nitride.4.16191][2023-01-29] - Changed `build_all_firmwares.sh` to fit GENERIC 256kb firmware images (@doegox) diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index 637215cc3..a8cc1414b 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -2084,8 +2084,8 @@ int EmGetCmd(uint8_t *received, uint16_t *len, uint8_t *par) { if (timer == 0) { timer = GetTickCount(); } else { - // 50ms no field --> card to idle state - if (GetTickCountDelta(timer) > 50) { + // 6ms no field --> card to idle state + if (GetTickCountDelta(timer) > 6) { return 2; } } From 0c9a64438f7e359ad2a1d48e7fafd32f7fac4380 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Fri, 5 May 2023 10:45:35 +0200 Subject: [PATCH 28/62] 256kb version is too large --- tools/build_all_firmwares.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tools/build_all_firmwares.sh b/tools/build_all_firmwares.sh index b38f557ab..4a0fe5947 100755 --- a/tools/build_all_firmwares.sh +++ b/tools/build_all_firmwares.sh @@ -22,8 +22,7 @@ echo "Destination: ${DEST:=firmware}" echo "Produce stats?: ${STATS:=false}" # Which parts to skip for the 256kb version? -SKIPS256="SKIP_HITAG=1 SKIP_LEGICRF=1 SKIP_FELICA=1 SKIP_EM4x50=1 SKIP_ISO14443b=1 SKIP_NFCBARCODE=1 SKIP_ZX8211=1" - +SKIPS256="SKIP_HITAG=1 SKIP_LEGICRF=1 SKIP_FELICA=1 SKIP_EM4x50=1 SKIP_ISO14443b=1 SKIP_NFCBARCODE=1 SKIP_ZX8211=1 SKIP_LF=1" make $MKFLAGS bootrom || exit 1 chmod 644 bootrom/obj/bootrom.elf From 52981476e208f31c9527dc1751ed47da550114a1 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Fri, 5 May 2023 10:47:02 +0200 Subject: [PATCH 29/62] fixed ndef parsing of signature version 1 records --- CHANGELOG.md | 1 + client/src/nfc/ndef.c | 68 ++++++++++++++++++++++++++++++------------- 2 files changed, 49 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1795ed0a1..35e5f756c 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] + - Fixed `nfc decode` - now handles NDEF Signature version1 records better (@iceman1001) - Added new standalone mode `LF_MULTIHID` - HID26 (H1031) multi simulator (@flamebarke) - Changed `hf 14b dump --ns` - now supports `no save` of card memory (@iceman1001) - Changed `hf mfu dump --ns` - now supports `no save` of card memory (@iceman1001) diff --git a/client/src/nfc/ndef.c b/client/src/nfc/ndef.c index b995a3e19..99df7e419 100644 --- a/client/src/nfc/ndef.c +++ b/client/src/nfc/ndef.c @@ -289,22 +289,35 @@ static int ndef_print_signature(uint8_t *data, uint8_t data_len, uint8_t *signat } static int ndefDecodeSig1(uint8_t *sig, size_t siglen) { - size_t indx = 1; + size_t indx = 1; uint8_t sigType = sig[indx] & 0x7f; bool sigURI = sig[indx] & 0x80; + indx++; - PrintAndLogEx(SUCCESS, "\tsignature type: %s", ((sigType < stNA) ? ndefSigType_s[sigType] : ndefSigType_s[stNA])); - PrintAndLogEx(SUCCESS, "\tsignature uri: %s", (sigURI ? "present" : "not present")); + PrintAndLogEx(SUCCESS, "\tSignature type... " _YELLOW_("%s"), ((sigType < stNA) ? ndefSigType_s[sigType] : ndefSigType_s[stNA])); + PrintAndLogEx(SUCCESS, "\tSignature URI.... " _YELLOW_("%s"), (sigURI ? "present" : "not present")); + + if (sigType == 0 && sigURI == false) { + PrintAndLogEx(INFO, "\tRecord should be considered a start marker"); + } + if (sigType == 0 && sigURI) { + PrintAndLogEx(INFO, _RED_("\tSignature record is invalid")); + } + + uint16_t intsiglen = MemBeToUint2byte(sig + indx); + indx += 2; - size_t intsiglen = (sig[indx + 1] << 8) + sig[indx + 2]; // ecdsa 0x04 if (sigType == stECDSA_P192 || sigType == stECDSA_P256) { - indx += 3; + int slen = 24; - if (sigType == stECDSA_P256) + if (sigType == stECDSA_P256) { slen = 32; - PrintAndLogEx(SUCCESS, "\tsignature [%zu]: %s", intsiglen, sprint_hex_inrow(&sig[indx], intsiglen)); + } + + PrintAndLogEx(SUCCESS, "\tSignature [%u]...", intsiglen); + print_hex_noascii_break(&sig[indx], intsiglen, 32); uint8_t rval[300] = {0}; uint8_t sval[300] = {0}; @@ -313,38 +326,53 @@ static int ndefDecodeSig1(uint8_t *sig, size_t siglen) { PrintAndLogEx(SUCCESS, "\t\tr: %s", sprint_hex(rval + 32 - slen, slen)); PrintAndLogEx(SUCCESS, "\t\ts: %s", sprint_hex(sval + 32 - slen, slen)); } + } else { + PrintAndLogEx(SUCCESS, "\tSignature [%u]...", intsiglen); + print_hex_noascii_break(&sig[indx], intsiglen, 32); } + indx += intsiglen; if (sigURI) { - size_t intsigurilen = (sig[indx] << 8) + sig[indx + 1]; + + uint16_t intsigurilen = MemBeToUint2byte(sig + indx); indx += 2; - PrintAndLogEx(SUCCESS, "\tsignature uri [%zu]: %.*s", intsigurilen, (int)intsigurilen, &sig[indx]); + + PrintAndLogEx(SUCCESS, "\tSignature URI... " _YELLOW_("%.*s"), (int)intsigurilen, &sig[indx]); indx += intsigurilen; } + // CERTIFICATE SECTION + PrintAndLogEx(INFO, ""); + PrintAndLogEx(INFO, _CYAN_("Certificate")); + uint8_t certFormat = (sig[indx] >> 4) & 0x07; uint8_t certCount = sig[indx] & 0x0f; bool certURI = sig[indx] & 0x80; + indx++; - PrintAndLogEx(SUCCESS, "\tcertificate format: %s", ((certFormat < sfNA) ? ndefCertificateFormat_s[certFormat] : ndefCertificateFormat_s[sfNA])); - PrintAndLogEx(SUCCESS, "\tcertificates count: %d", certCount); + PrintAndLogEx(SUCCESS, "\tFormat............ " _YELLOW_("%s"), ((certFormat < sfNA) ? ndefCertificateFormat_s[certFormat] : ndefCertificateFormat_s[sfNA])); + if (certCount) { + PrintAndLogEx(SUCCESS, "\tNum of certs#..... " _YELLOW_("%d"), certCount); + } // print certificates - indx++; - for (int i = 0; i < certCount; i++) { - size_t intcertlen = (sig[indx + 1] << 8) + sig[indx + 2]; + for (uint8_t i = 0; i < certCount; i++) { + uint16_t intcertlen = MemBeToUint2byte(sig + indx); indx += 2; - PrintAndLogEx(SUCCESS, "\tcertificate %d [%zu]: %s", i + 1, intcertlen, sprint_hex_inrow(&sig[indx], intcertlen)); + PrintAndLogEx(INFO, ""); + PrintAndLogEx(SUCCESS, "\tCertificate %u [%u]...", i + 1, intcertlen); + print_hex_noascii_break(&sig[indx], intcertlen, 32); + indx += intcertlen; } - // have certificate uri + // print certificate uri if ((indx <= siglen) && certURI) { - size_t inturilen = (sig[indx] << 8) + sig[indx + 1]; + uint16_t inturilen = MemBeToUint2byte(sig + indx); indx += 2; - PrintAndLogEx(SUCCESS, "\tcertificate uri [%zu]: %.*s", inturilen, (int)inturilen, &sig[indx]); + PrintAndLogEx(SUCCESS, "\tCertificate URI... " _YELLOW_("%.*s"), (int)inturilen, &sig[indx]); } return PM3_SUCCESS; @@ -417,9 +445,9 @@ static int ndefDecodeSig2(uint8_t *sig, size_t siglen) { } static int ndefDecodeSig(uint8_t *sig, size_t siglen) { - PrintAndLogEx(SUCCESS, "\tsignature version : \t" _GREEN_("0x%02x"), sig[0]); + PrintAndLogEx(SUCCESS, "\tVersion... " _GREEN_("0x%02x"), sig[0]); if (sig[0] != 0x01 && sig[0] != 0x20) { - PrintAndLogEx(ERR, "signature version unknown."); + PrintAndLogEx(ERR, _RED_("Version unknown")); return PM3_ESOFT; } From 3e293b4bc8dbce24c81f18818ad330bf1159ab72 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Fri, 5 May 2023 12:33:14 +0200 Subject: [PATCH 30/62] changed nfc decode to handle external records and if the record happens to be estonian ekaart also ASN1 decode it --- CHANGELOG.md | 1 + client/src/nfc/ndef.c | 63 ++++++++++++++++++++++++++++++++++++------- client/src/nfc/ndef.h | 3 ++- 3 files changed, 56 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 35e5f756c..e20b440f8 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 `nfc decode` - now handles EXTERNAL RECORDS better (@iceman1001) - Fixed `nfc decode` - now handles NDEF Signature version1 records better (@iceman1001) - Added new standalone mode `LF_MULTIHID` - HID26 (H1031) multi simulator (@flamebarke) - Changed `hf 14b dump --ns` - now supports `no save` of card memory (@iceman1001) diff --git a/client/src/nfc/ndef.c b/client/src/nfc/ndef.c index 99df7e419..a97d5bb55 100644 --- a/client/src/nfc/ndef.c +++ b/client/src/nfc/ndef.c @@ -39,8 +39,6 @@ #define NDEF_VCARDTEXT "text/vcard" #define NDEF_XVCARDTEXT "text/x-vcard" - - static const char *TypeNameFormat_s[] = { "Empty Record", "Well Known Record", @@ -295,8 +293,8 @@ static int ndefDecodeSig1(uint8_t *sig, size_t siglen) { bool sigURI = sig[indx] & 0x80; indx++; - PrintAndLogEx(SUCCESS, "\tSignature type... " _YELLOW_("%s"), ((sigType < stNA) ? ndefSigType_s[sigType] : ndefSigType_s[stNA])); - PrintAndLogEx(SUCCESS, "\tSignature URI.... " _YELLOW_("%s"), (sigURI ? "present" : "not present")); + PrintAndLogEx(SUCCESS, "\tType...... " _YELLOW_("%s"), ((sigType < stNA) ? ndefSigType_s[sigType] : ndefSigType_s[stNA])); + PrintAndLogEx(SUCCESS, "\tURI....... " _YELLOW_("%s"), (sigURI ? "present" : "not present")); if (sigType == 0 && sigURI == false) { PrintAndLogEx(INFO, "\tRecord should be considered a start marker"); @@ -327,7 +325,7 @@ static int ndefDecodeSig1(uint8_t *sig, size_t siglen) { PrintAndLogEx(SUCCESS, "\t\ts: %s", sprint_hex(sval + 32 - slen, slen)); } } else { - PrintAndLogEx(SUCCESS, "\tSignature [%u]...", intsiglen); + PrintAndLogEx(SUCCESS, "\tData [%u]...", intsiglen); print_hex_noascii_break(&sig[indx], intsiglen, 32); } @@ -817,7 +815,7 @@ static int ndefDecodeMime_bt(NDEFHeader_t *ndef) { return PM3_SUCCESS; } PrintAndLogEx(INFO, "Type............ " _YELLOW_("%.*s"), (int)ndef->TypeLen, ndef->Type); - uint16_t ooblen = (ndef->Payload[1] << 8 | ndef->Payload[0]); + uint16_t ooblen = MemBeToUint2byte(ndef->Payload); PrintAndLogEx(INFO, "OOB data len.... %u", ooblen); PrintAndLogEx(INFO, "BT MAC.......... " _YELLOW_("%s"), sprint_hex(ndef->Payload + 2, 6)); // Let's check payload[8]. Tells us a bit about the UUID's. If 0x07 then it tells us a service UUID is 128bit @@ -856,6 +854,38 @@ static int ndefDecodeMime_bt(NDEFHeader_t *ndef) { return PM3_SUCCESS; } +// https://raw.githubusercontent.com/haldean/ndef/master/docs/NFCForum-TS-RTD_1.0.pdf +static int ndefDecodeExternal_record(NDEFHeader_t *ndef) { + + if (ndef->TypeLen == 0) { + PrintAndLogEx(INFO, "no type"); + return PM3_SUCCESS; + } + + if (ndef->PayloadLen == 0) { + PrintAndLogEx(INFO, "no payload"); + return PM3_SUCCESS; + } + + PrintAndLogEx(INFO + , " URN... " _GREEN_("urn:nfc:ext:%.*s") + , (int)ndef->TypeLen + , ndef->Type + ); + + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "Payload [%u]...", ndef->PayloadLen); + print_hex_noascii_break(ndef->Payload, ndef->PayloadLen, 32); + + // do a character check? + if (!strncmp((char *)ndef->Type, "pilet.ee:ekaart:2", ndef->TypeLen)) { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(SUCCESS, _GREEN_("Ekaart detected") " - Trying ASN1 decode..."); + asn1_print(ndef->Payload, ndef->PayloadLen, " "); + } + return PM3_SUCCESS; +} + static int ndefDecodePayload(NDEFHeader_t *ndef, bool verbose) { PrintAndLogEx(INFO, ""); @@ -863,7 +893,7 @@ static int ndefDecodePayload(NDEFHeader_t *ndef, bool verbose) { case tnfEmptyRecord: PrintAndLogEx(INFO, "Empty Record"); if (ndef->TypeLen != 0 || ndef->IDLen != 0 || ndef->PayloadLen != 0) { - PrintAndLogEx(FAILED, "unexpected data in TNF_EMPTY record"); + PrintAndLogEx(FAILED, "unexpected data in empty record"); break; } break; @@ -959,20 +989,33 @@ static int ndefDecodePayload(NDEFHeader_t *ndef, bool verbose) { } case tnfAbsoluteURIRecord: PrintAndLogEx(INFO, "Absolute URI Record"); - PrintAndLogEx(INFO, " payload : %.*s", (int)ndef->PayloadLen, ndef->Payload); + PrintAndLogEx(INFO, " payload : " _YELLOW_("%.*s"), (int)ndef->PayloadLen, ndef->Payload); break; case tnfExternalRecord: PrintAndLogEx(INFO, "External Record"); - PrintAndLogEx(INFO, "- decoder to be impl -"); + ndefDecodeExternal_record(ndef); break; case tnfUnknownRecord: PrintAndLogEx(INFO, "Unknown Record"); - PrintAndLogEx(INFO, "- decoder to be impl -"); + if (ndef->TypeLen != 0) { + PrintAndLogEx(FAILED, "unexpected type field"); + break; + } break; case tnfUnchangedRecord: PrintAndLogEx(INFO, "Unchanged Record"); PrintAndLogEx(INFO, "- decoder to be impl -"); break; + case tnfReservedRecord: + PrintAndLogEx(INFO, "Reserved Record"); + if (ndef->TypeLen != 0) { + PrintAndLogEx(FAILED, "unexpected type field"); + break; + } + break; + default: + PrintAndLogEx(FAILED, "unexpected tnf value... 0x%02x", ndef->TypeNameFormat); + break; } PrintAndLogEx(INFO, ""); return PM3_SUCCESS; diff --git a/client/src/nfc/ndef.h b/client/src/nfc/ndef.h index 478c28e0a..643bb70a7 100644 --- a/client/src/nfc/ndef.h +++ b/client/src/nfc/ndef.h @@ -31,7 +31,8 @@ typedef enum { tnfAbsoluteURIRecord = 0x03, tnfExternalRecord = 0x04, tnfUnknownRecord = 0x05, - tnfUnchangedRecord = 0x06 + tnfUnchangedRecord = 0x06, + tnfReservedRecord = 0x07, } TypeNameFormat_t; typedef enum { From ed555be1804b9f98d59351541a16ee171939d2a8 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Fri, 5 May 2023 13:18:53 +0200 Subject: [PATCH 31/62] fix wrong copying when skipping sector trailers --- client/src/mifare/mifarehost.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/src/mifare/mifarehost.c b/client/src/mifare/mifarehost.c index 9e9f674e1..3b37182fe 100644 --- a/client/src/mifare/mifarehost.c +++ b/client/src/mifare/mifarehost.c @@ -1467,12 +1467,12 @@ int convert_mfc_2_arr(uint8_t *in, uint16_t ilen, uint8_t *out, uint16_t *olen) if (mfIsSectorTrailer(blockno) == false) { memcpy(out, in, MFBLOCK_SIZE); + out += MFBLOCK_SIZE; + *olen += MFBLOCK_SIZE; } blockno++; - out += MFBLOCK_SIZE; in += MFBLOCK_SIZE; ilen -= MFBLOCK_SIZE; - *olen += MFBLOCK_SIZE; } return PM3_SUCCESS; } From 78f097cb5581081f0ee42958b13c41d4b487aa90 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Fri, 5 May 2023 13:20:52 +0200 Subject: [PATCH 32/62] the nfc decode now handling MFC dump files, it detects the MAD key and if so removes all sector trailers. this means a lot of zeros in the end might be there. And ndef prints a bunch of empty records. Better than before atleast --- CHANGELOG.md | 1 + client/src/cmdnfc.c | 13 +++++++++++++ 2 files changed, 14 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e20b440f8..dff4bcf59 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 `nfc decode -f` - now can detect and convert MFC dumpfiles to NDEF byte arrays (@iceman1001) - Changed `nfc decode` - now handles EXTERNAL RECORDS better (@iceman1001) - Fixed `nfc decode` - now handles NDEF Signature version1 records better (@iceman1001) - Added new standalone mode `LF_MULTIHID` - HID26 (H1031) multi simulator (@flamebarke) diff --git a/client/src/cmdnfc.c b/client/src/cmdnfc.c index df87ee6f8..6f9561783 100644 --- a/client/src/cmdnfc.c +++ b/client/src/cmdnfc.c @@ -30,6 +30,8 @@ #include "cmdhftopaz.h" #include "cmdnfc.h" #include "fileutils.h" +#include "mifare/mifaredefault.h" +#include "mifare/mad.h" void print_type4_cc_info(uint8_t *d, uint8_t n) { if (n < 0x0F) { @@ -111,6 +113,17 @@ static int CmdNfcDecode(const char *Cmd) { return res; } + // convert from MFC dump file to a pure NDEF byte array + if (HasMADKey(dump)) { + PrintAndLogEx(SUCCESS, "MFC dump file detected. Converting..."); + uint8_t ndef[4096] = {0}; + uint16_t ndeflen = 0; + uint8_t skip = (4 * MFBLOCK_SIZE); + convert_mfc_2_arr(dump + skip, bytes_read - skip, ndef, &ndeflen); + memcpy(dump, ndef, ndeflen); + bytes_read = ndeflen; + } + res = NDEFDecodeAndPrint(dump, bytes_read, verbose); if (res != PM3_SUCCESS) { PrintAndLogEx(INFO, "Trying to parse NDEF records w/o NDEF header"); From 3b68acd2864b3cd401b07da4b98fa2bfc080e529 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Fri, 5 May 2023 19:37:35 +0200 Subject: [PATCH 33/62] comment out duplicate but kept it since documentation --- client/dictionaries/mfc_default_keys.dic | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/dictionaries/mfc_default_keys.dic b/client/dictionaries/mfc_default_keys.dic index 4af8cf64a..b51989bda 100644 --- a/client/dictionaries/mfc_default_keys.dic +++ b/client/dictionaries/mfc_default_keys.dic @@ -2112,7 +2112,7 @@ D23C1CB1216E # 20230413-78, Caesars A1D92F808CAF # 20230413-79, The Cosmopolitan, Vegas -96A301BCE267 +# 96A301BCE267 # 20230413-80, Aria 1153C319B4F8 # 20230413-81, Aria From cb0a447600c66d5f8a4792b1b3aa3ae898f45c66 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Fri, 5 May 2023 22:24:53 +0200 Subject: [PATCH 34/62] bt handover req/select and reverse BT MAC --- client/src/nfc/ndef.c | 57 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 52 insertions(+), 5 deletions(-) diff --git a/client/src/nfc/ndef.c b/client/src/nfc/ndef.c index a97d5bb55..b0a32daec 100644 --- a/client/src/nfc/ndef.c +++ b/client/src/nfc/ndef.c @@ -499,6 +499,51 @@ static int ndefDecodePayloadDeviceInfo(uint8_t *payload, size_t len) { return PM3_SUCCESS; } +static int ndefDecodePayloadHandoverRequest(uint8_t *payload, size_t len) { + if (payload == NULL) + return PM3_EINVARG; + if (len < 1) + return PM3_EINVARG; + + PrintAndLogEx(INFO, _CYAN_("Handover Request")); + uint8_t *p = payload; + uint8_t major = (*(p) >> 4) & 0x0F; + uint8_t minor = *(p) & 0x0F; + p++; + + PrintAndLogEx(INFO, "Version....... " _YELLOW_("%u.%u"), major, minor); + if (major != 1 && minor != 2) { + PrintAndLogEx(FAILED, "Wrong version numbers"); + } + + uint16_t collision = MemBeToUint2byte(p); + p += 2; + PrintAndLogEx(INFO, "Collision Resolution... " _YELLOW_("%u"), collision); + PrintAndLogEx(NORMAL, ""); + + return PM3_SUCCESS; +} + +static int ndefDecodePayloadHandoverSelect(uint8_t *payload, size_t len) { + if (payload == NULL) + return PM3_EINVARG; + if (len < 1) + return PM3_EINVARG; + + PrintAndLogEx(INFO, _CYAN_("Handover select")); + + uint8_t *p = payload; + uint8_t major = (*(p) >> 4) & 0x0F; + uint8_t minor = *(p) & 0x0F; + p++; + PrintAndLogEx(INFO, "Version....... " _YELLOW_("%u.%u"), major, minor); + if (major != 1 && minor != 2) { + PrintAndLogEx(FAILED, "Wrong version numbers"); + } + PrintAndLogEx(NORMAL, ""); + return PM3_SUCCESS; +} + static int ndefDecodePayloadSmartPoster(uint8_t *ndef, size_t ndeflen, bool print, bool verbose) { if (print) { PrintAndLogEx(INFO, _YELLOW_("Well Known Record - Smartposter {")); @@ -817,7 +862,11 @@ static int ndefDecodeMime_bt(NDEFHeader_t *ndef) { PrintAndLogEx(INFO, "Type............ " _YELLOW_("%.*s"), (int)ndef->TypeLen, ndef->Type); uint16_t ooblen = MemBeToUint2byte(ndef->Payload); PrintAndLogEx(INFO, "OOB data len.... %u", ooblen); - PrintAndLogEx(INFO, "BT MAC.......... " _YELLOW_("%s"), sprint_hex(ndef->Payload + 2, 6)); + + uint8_t rev[6] = {0}; + reverse_array_copy(ndef->Payload + 2, 6, rev); + PrintAndLogEx(INFO, "BT MAC.......... " _YELLOW_("%s"), sprint_hex(rev, sizeof(rev))); + // Let's check payload[8]. Tells us a bit about the UUID's. If 0x07 then it tells us a service UUID is 128bit switch (ndef->Payload[8]) { case 0x02: @@ -942,13 +991,11 @@ static int ndefDecodePayload(NDEFHeader_t *ndef, bool verbose) { } if (!strncmp((char *)ndef->Type, "Hr", ndef->TypeLen)) { - PrintAndLogEx(INFO, _CYAN_("Handover request")); - PrintAndLogEx(INFO, "- decoder to be impl -"); + ndefDecodePayloadHandoverRequest(ndef->Payload, ndef->PayloadLen); } if (!strncmp((char *)ndef->Type, "Hs", ndef->TypeLen)) { - PrintAndLogEx(INFO, _CYAN_("Handover select")); - PrintAndLogEx(INFO, "- decoder to be impl -"); + ndefDecodePayloadHandoverSelect(ndef->Payload, ndef->PayloadLen); } if (!strncmp((char *)ndef->Type, "ac", ndef->TypeLen)) { From 27576be5c25241ea7bf2b7a20b3f954e1feb3e83 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Fri, 5 May 2023 22:26:44 +0200 Subject: [PATCH 35/62] trying to extract ndef data from dumps by looking at MAD --- client/src/cmdhfmf.c | 18 ------------ client/src/cmdnfc.c | 11 +++++-- client/src/mifare/mad.c | 48 +++++++++++++++++++++++++++++++ client/src/mifare/mad.h | 1 + client/src/mifare/mifaredefault.h | 21 ++++++++++++-- 5 files changed, 77 insertions(+), 22 deletions(-) diff --git a/client/src/cmdhfmf.c b/client/src/cmdhfmf.c index a647d90c2..dc0dbe412 100644 --- a/client/src/cmdhfmf.c +++ b/client/src/cmdhfmf.c @@ -18,7 +18,6 @@ #include "cmdhfmf.h" #include - #include "cmdparser.h" // command_t #include "commonutil.h" // ARRAYLEN #include "comms.h" // clearCommandBuffer @@ -38,23 +37,6 @@ #include "wiegand_formats.h" #include "wiegand_formatutils.h" -#define MIFARE_4K_MAXBLOCK 256 -#define MIFARE_2K_MAXBLOCK 128 -#define MIFARE_1K_MAXBLOCK 64 -#define MIFARE_MINI_MAXBLOCK 20 - -#define MIFARE_4K_MAXSECTOR 40 -#define MIFARE_2K_MAXSECTOR 32 -#define MIFARE_1K_MAXSECTOR 16 -#define MIFARE_MINI_MAXSECTOR 5 - -#define MIFARE_4K_MAX_BYTES 4096 -#define MIFARE_2K_MAX_BYTES 2048 -#define MIFARE_1K_MAX_BYTES 1024 -#define MIFARE_MINI_MAX_BYTES 320 - -#define MIFARE_KEY_SIZE 6 - static int CmdHelp(const char *Cmd); /* diff --git a/client/src/cmdnfc.c b/client/src/cmdnfc.c index 6f9561783..221b33020 100644 --- a/client/src/cmdnfc.c +++ b/client/src/cmdnfc.c @@ -118,8 +118,15 @@ static int CmdNfcDecode(const char *Cmd) { PrintAndLogEx(SUCCESS, "MFC dump file detected. Converting..."); uint8_t ndef[4096] = {0}; uint16_t ndeflen = 0; - uint8_t skip = (4 * MFBLOCK_SIZE); - convert_mfc_2_arr(dump + skip, bytes_read - skip, ndef, &ndeflen); + // uint8_t skip = (4 * MFBLOCK_SIZE); + // convert_mfc_2_arr(dump + skip, bytes_read - skip, ndef, &ndeflen); + + if (convert_mad_to_arr(dump, bytes_read, ndef, &ndeflen) != PM3_SUCCESS) { + PrintAndLogEx(FAILED, "Failed converting, aborting..."); + free(dump); + return PM3_ESOFT; + } + memcpy(dump, ndef, ndeflen); bytes_read = ndeflen; } diff --git a/client/src/mifare/mad.c b/client/src/mifare/mad.c index 023f3cbf6..49d3de065 100644 --- a/client/src/mifare/mad.c +++ b/client/src/mifare/mad.c @@ -421,3 +421,51 @@ int DetectHID(uint8_t *d, uint16_t manufacture) { return -1; } + +int convert_mad_to_arr(uint8_t *in, uint16_t ilen, uint8_t *out, uint16_t *olen) { + + if (in == NULL || out == NULL || ilen == 0 ) { + return PM3_EINVARG; + } + + // MAD detection + if (HasMADKey(in) == false) { + PrintAndLogEx(FAILED, "No MAD key was detected in the dump file"); + return PM3_ESOFT; + } + + uint8_t sector0[MFBLOCK_SIZE * 4] = {0}; + uint8_t sector10[MFBLOCK_SIZE * 4] = {0}; + + memcpy(sector0, in, sizeof(sector0)); + if (ilen == MIFARE_4K_MAX_BYTES) { + memcpy(sector10, in + (MF_MAD2_SECTOR * 4 * MFBLOCK_SIZE), sizeof(sector10)); + } + + uint16_t mad[7 + 8 + 8 + 8 + 8] = {0}; + size_t madlen = 0; + if (MADDecode(sector0, sector10, mad, &madlen, false)) { + PrintAndLogEx(ERR, "can't decode MAD"); + return PM3_ESOFT; + } + + uint16_t ndef_aid = 0xE103; + for (int i = 0; i < madlen; i++) { + if (ndef_aid == mad[i]) { + uint8_t tmp[MFBLOCK_SIZE * 4] = {0}; + memset(tmp, 0x00, sizeof(tmp)); + + // sector i dump (skip first sector +1) + memcpy(tmp, in + (i + 1) * sizeof(tmp), sizeof(tmp)); + + // debug print + // print_hex_noascii_break(tmp, sizeof(tmp) - MFBLOCK_SIZE, MFBLOCK_SIZE); + + // copy to out (skip ST) + memcpy(out, tmp, sizeof(tmp) - MFBLOCK_SIZE); + out += sizeof(tmp) - MFBLOCK_SIZE; + *olen += sizeof(tmp) -MFBLOCK_SIZE; + } + } + return PM3_SUCCESS; +} \ No newline at end of file diff --git a/client/src/mifare/mad.h b/client/src/mifare/mad.h index e1ebec62c..d1f5240f4 100644 --- a/client/src/mifare/mad.h +++ b/client/src/mifare/mad.h @@ -30,4 +30,5 @@ int MADCardHolderInfoDecode(uint8_t *data, size_t datalen, bool verbose); void MADPrintHeader(void); bool HasMADKey(uint8_t *d); int DetectHID(uint8_t *d, uint16_t manufacture); +int convert_mad_to_arr(uint8_t *in, uint16_t ilen, uint8_t *out, uint16_t *olen); #endif // _MAD_H_ diff --git a/client/src/mifare/mifaredefault.h b/client/src/mifare/mifaredefault.h index 7ce83e29f..fe11a2a27 100644 --- a/client/src/mifare/mifaredefault.h +++ b/client/src/mifare/mifaredefault.h @@ -21,8 +21,25 @@ #include "common.h" -#define MFKEY_SIZE 6 -#define MFBLOCK_SIZE 16 +#define MFKEY_SIZE 6 +#define MFBLOCK_SIZE 16 + +#define MIFARE_4K_MAXBLOCK 256 +#define MIFARE_2K_MAXBLOCK 128 +#define MIFARE_1K_MAXBLOCK 64 +#define MIFARE_MINI_MAXBLOCK 20 + +#define MIFARE_4K_MAXSECTOR 40 +#define MIFARE_2K_MAXSECTOR 32 +#define MIFARE_1K_MAXSECTOR 16 +#define MIFARE_MINI_MAXSECTOR 5 + +#define MIFARE_4K_MAX_BYTES 4096 +#define MIFARE_2K_MAX_BYTES 2048 +#define MIFARE_1K_MAX_BYTES 1024 +#define MIFARE_MINI_MAX_BYTES 320 + +#define MIFARE_KEY_SIZE 6 static const uint64_t g_mifare_default_keys[] = { 0xffffffffffff, // Default key (first key used by program if no user defined key) From f5e93c0a1cf89c21c46932f65a59d4360fb5da1c Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Fri, 5 May 2023 22:27:00 +0200 Subject: [PATCH 36/62] added a test for ndef signatures --- tools/pm3_tests.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/pm3_tests.sh b/tools/pm3_tests.sh index f116de2b1..3320d9866 100755 --- a/tools/pm3_tests.sh +++ b/tools/pm3_tests.sh @@ -388,6 +388,7 @@ while true; do if ! CheckExecute "nfc decode test - device info" "$CLIENTBIN -c 'nfc decode -d d1025744690004536f6e79010752432d533338300220426c61636b204e46432052656164657220636f6e6e656374656420746f2050430310123e4567e89b12d3a45642665544000004124e464320506f72742d3130302076312e3032'" "NFC Port-100 v1.02"; then break; fi if ! CheckExecute "nfc decode test - vcard" "$CLIENTBIN -c 'nfc decode -d d20ca3746578742f782d7643617264424547494e3a56434152440a56455253494f4e3a332e300a4e3a43687269733b4963656d616e3b3b3b0a464e3a476f7468656e627572670a5245563a323032312d30362d32345432303a31353a30385a0a6974656d322e582d4142444154453b747970653d707265663a323032302d30362d32340a4954454d322e582d41424c4142454c3a5f24213c416e6e69766572736172793e21245f0a454e443a56434152440a'" "END:VCARD"; then break; fi if ! CheckExecute "nfc decode test - apple wallet" "$CLIENTBIN -c 'nfc decode -d 031AD10116550077616C6C65743A2F2F61637469766174652F6E6663FE'" "activate/nfc"; then break; fi + if ! CheckExecute "nfc decode test - signature" "$CLIENTBIN -c 'nfc decode -d 03FF010194113870696C65742E65653A656B616172743A3266195F26063132303832325904202020205F28033233335F2701316E1B5A13333038363439303039303030323636343030355304EBF2CE704103000000AC536967010200803A2448FCA7D354A654A81BD021150D1A152D1DF4D7A55D2B771F12F094EAB6E5E10F2617A2F8DAD4FD38AFF8EA39B71C19BD42618CDA86EE7E144636C8E0E7CFC4096E19C3680E09C78A0CDBC05DA2D698E551D5D709717655E56FE3676880B897D2C70DF5F06ECE07C71435255144F8EE41AF110E7B180DA0E6C22FB8FDEF61800025687474703A2F2F70696C65742E65652F6372742F33303836343930302D303030312E637274FE'" "30864900-0001.crt"; then break; fi echo -e "\n${C_BLUE}Testing LF:${C_NC}" if ! CheckExecute "lf AWID test" "$CLIENTBIN -c 'data load -f traces/lf_AWID-15-259.pm3;lf search -1'" "AWID ID found"; then break; fi From 31b01fff5a7634424d27123883fff47b341996fb Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Sat, 6 May 2023 06:55:10 +0200 Subject: [PATCH 37/62] clearning variable before each call to hardnested --- client/src/cmdhfmf.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/client/src/cmdhfmf.c b/client/src/cmdhfmf.c index dc0dbe412..c9f473033 100644 --- a/client/src/cmdhfmf.c +++ b/client/src/cmdhfmf.c @@ -2841,7 +2841,7 @@ tryNested: } break; } - case PM3_ESTATIC_NONCE: + case PM3_ESTATIC_NONCE: { PrintAndLogEx(ERR, "Error: Static encrypted nonce detected. Aborted\n"); e_sector[current_sector_i].Key[current_key_type_i] = 0xffffffffffff;; @@ -2854,6 +2854,7 @@ tryNested: free(e_sector); free(fptr); return isOK; + } case PM3_SUCCESS: { calibrate = false; e_sector[current_sector_i].Key[current_key_type_i] = bytes_to_num(tmp_key, 6); @@ -2878,6 +2879,7 @@ tryHardnested: // If the nested attack fails then we try the hardnested attack slow ? "Yes" : "No"); } + foundkey = 0; isOK = mfnestedhard(mfFirstBlockOfSector(sectorno), keytype, key, mfFirstBlockOfSector(current_sector_i), current_key_type_i, NULL, false, false, slow, 0, &foundkey, NULL); DropField(); if (isOK) { From a3fa6c3df5c0980f2ee53f5dba6c293377eab308 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Sat, 6 May 2023 14:13:44 +0200 Subject: [PATCH 38/62] fixed ACL when recoving keys from SIM --- client/src/cmdhfmf.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/client/src/cmdhfmf.c b/client/src/cmdhfmf.c index c9f473033..c158bde19 100644 --- a/client/src/cmdhfmf.c +++ b/client/src/cmdhfmf.c @@ -3606,9 +3606,6 @@ void showSectorTable(sector_t *k_sector, uint8_t k_sectorsCount) { void readerAttack(sector_t *k_sector, uint8_t k_sectorsCount, nonces_t data, bool setEmulatorMem, bool verbose) { - uint64_t key = 0; - bool success = false; - if (k_sector == NULL) { int32_t res = initSectorTable(&k_sector, k_sectorsCount); if (res != k_sectorsCount) { @@ -3617,8 +3614,8 @@ void readerAttack(sector_t *k_sector, uint8_t k_sectorsCount, nonces_t data, boo } } - success = mfkey32_moebius(&data, &key); - if (success) { + uint64_t key = 0; + if (mfkey32_moebius(&data, &key)) { uint8_t sector = data.sector; uint8_t keytype = data.keytype; @@ -3633,7 +3630,7 @@ void readerAttack(sector_t *k_sector, uint8_t k_sectorsCount, nonces_t data, boo //set emulator memory for keys if (setEmulatorMem) { - uint8_t memBlock[16] = {0, 0, 0, 0, 0, 0, 0xff, 0x0F, 0x80, 0x69, 0, 0, 0, 0, 0, 0}; + uint8_t memBlock[16] = {0, 0, 0, 0, 0, 0, 0xFF, 0x07, 0x80, 0x69, 0, 0, 0, 0, 0, 0}; num_to_bytes(k_sector[sector].Key[0], 6, memBlock); num_to_bytes(k_sector[sector].Key[1], 6, memBlock + 10); //iceman, guessing this will not work so well for 4K tags. From 02248162086171be16f802837050f84f7d506883 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Sat, 6 May 2023 14:31:19 +0200 Subject: [PATCH 39/62] refactoring the return value of function --- client/src/cmdhfmf.c | 37 ++++++++++++++----------------------- client/src/cmdhfmf.h | 4 ++-- 2 files changed, 16 insertions(+), 25 deletions(-) diff --git a/client/src/cmdhfmf.c b/client/src/cmdhfmf.c index c158bde19..b71a5ffc1 100644 --- a/client/src/cmdhfmf.c +++ b/client/src/cmdhfmf.c @@ -134,12 +134,12 @@ static char *GenerateFilename(const char *prefix, const char *suffix) { return fptr; } -static int32_t initSectorTable(sector_t **src, int32_t items) { +static int initSectorTable(sector_t **src, size_t items) { (*src) = calloc(items, sizeof(sector_t)); if (*src == NULL) - return -1; + return PM3_EMALLOC; // empty e_sector for (int i = 0; i < items; ++i) { @@ -148,7 +148,7 @@ static int32_t initSectorTable(sector_t **src, int32_t items) { (*src)[i].foundKey[j] = false; } } - return items; + return PM3_SUCCESS; } static void decode_print_st(uint16_t blockno, uint8_t *data) { @@ -2390,7 +2390,6 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { iso14a_card_select_t card; memcpy(&card, (iso14a_card_select_t *)resp.data.asBytes, sizeof(iso14a_card_select_t)); - // detect MFC EV1 Signature bool is_ev1 = detect_mfc_ev1_signature(); if (is_ev1) { @@ -2400,9 +2399,7 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { // create/initialize key storage structure uint32_t e_sector_size = sector_cnt > sectorno ? sector_cnt : sectorno + 1; - res = initSectorTable(&e_sector, e_sector_size); - if (res != e_sector_size) { - free(e_sector); + if (initSectorTable(&e_sector, e_sector_size) != PM3_SUCCESS) { return PM3_EMALLOC; } @@ -3195,8 +3192,7 @@ static int CmdHF14AMfChk_fast(const char *Cmd) { // create/initialize key storage structure sector_t *e_sector = NULL; - int32_t res = initSectorTable(&e_sector, sectorsCnt); - if (res != sectorsCnt) { + if (initSectorTable(&e_sector, sectorsCnt) != PM3_SUCCESS) { free(keyBlock); return PM3_EMALLOC; } @@ -3231,7 +3227,7 @@ static int CmdHF14AMfChk_fast(const char *Cmd) { if (size == keycnt - i) lastChunk = true; - res = mfCheckKeys_fast(sectorsCnt, firstChunk, lastChunk, strategy, size, keyBlock + (i * 6), e_sector, false); + int res = mfCheckKeys_fast(sectorsCnt, firstChunk, lastChunk, strategy, size, keyBlock + (i * 6), e_sector, false); if (firstChunk) firstChunk = false; @@ -3438,8 +3434,7 @@ static int CmdHF14AMfChk(const char *Cmd) { // create/initialize key storage structure sector_t *e_sector = NULL; - int32_t res = initSectorTable(&e_sector, SectorsCnt); - if (res != SectorsCnt) { + if (initSectorTable(&e_sector, SectorsCnt) != PM3_SUCCESS) { free(keyBlock); return PM3_EMALLOC; } @@ -3597,19 +3592,18 @@ out: return PM3_SUCCESS; } -void showSectorTable(sector_t *k_sector, uint8_t k_sectorsCount) { +void showSectorTable(sector_t *k_sector, uint8_t k_sectors_cnt) { if (k_sector != NULL) { - printKeyTable(k_sectorsCount, k_sector); + printKeyTable(k_sectors_cnt, k_sector); free(k_sector); } } -void readerAttack(sector_t *k_sector, uint8_t k_sectorsCount, nonces_t data, bool setEmulatorMem, bool verbose) { +void readerAttack(sector_t *k_sector, uint8_t k_sectors_cnt, nonces_t data, bool setEmulatorMem, bool verbose) { + // init if needed if (k_sector == NULL) { - int32_t res = initSectorTable(&k_sector, k_sectorsCount); - if (res != k_sectorsCount) { - free(k_sector); + if (initSectorTable(&k_sector, k_sectors_cnt) != PM3_SUCCESS) { return; } } @@ -4566,12 +4560,9 @@ static int CmdHF14AMfEKeyPrn(const char *Cmd) { return PM3_EINVARG; } - sector_t *e_sector = NULL; - // create/initialize key storage structure - int32_t res = initSectorTable(&e_sector, sectors_cnt); - if (res != sectors_cnt) { - free(e_sector); + sector_t *e_sector = NULL; + if (initSectorTable(&e_sector, sectors_cnt) != PM3_SUCCESS) { return PM3_EMALLOC; } diff --git a/client/src/cmdhfmf.h b/client/src/cmdhfmf.h index 94610421b..62b95de4c 100644 --- a/client/src/cmdhfmf.h +++ b/client/src/cmdhfmf.h @@ -30,8 +30,8 @@ int CmdHFMFNDEFRead(const char *Cmd); // used by "nfc mf cread" int CmdHFMFNDEFFormat(const char *Cmd); // used by "nfc mf cformat" int CmdHFMFNDEFWrite(const char *Cmd); // used by "nfc mf cwrite" -void showSectorTable(sector_t *k_sector, uint8_t k_sectorsCount); -void readerAttack(sector_t *k_sector, uint8_t k_sectorsCount, nonces_t data, bool setEmulatorMem, bool verbose); +void showSectorTable(sector_t *k_sector, uint8_t k_sectors_cnt); +void readerAttack(sector_t *k_sector, uint8_t k_sectors_cnt, nonces_t data, bool setEmulatorMem, bool verbose); void printKeyTable(uint8_t sectorscnt, sector_t *e_sector); void printKeyTableEx(uint8_t sectorscnt, sector_t *e_sector, uint8_t start_sector); void printKeyTable_fast(uint8_t sectorscnt, icesector_t *e_sector, uint64_t bar, uint64_t foo); From d91b576034c8ce57eab940353d009099fd7234d7 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Sat, 6 May 2023 14:37:47 +0200 Subject: [PATCH 40/62] style --- client/src/cmdhfmf.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client/src/cmdhfmf.c b/client/src/cmdhfmf.c index b71a5ffc1..87c253525 100644 --- a/client/src/cmdhfmf.c +++ b/client/src/cmdhfmf.c @@ -2355,7 +2355,6 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { // Attack key storage variables uint8_t *keyBlock = NULL; uint32_t key_cnt = 0; - sector_t *e_sector; uint8_t tmp_key[6] = {0}; // Nested and Hardnested returned status @@ -2398,8 +2397,9 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { } // create/initialize key storage structure - uint32_t e_sector_size = sector_cnt > sectorno ? sector_cnt : sectorno + 1; - if (initSectorTable(&e_sector, e_sector_size) != PM3_SUCCESS) { + sector_t *e_sector = NULL; + uint32_t e_sector_cnt = (sector_cnt > sectorno) ? sector_cnt : sectorno + 1; + if (initSectorTable(&e_sector, e_sector_cnt) != PM3_SUCCESS) { return PM3_EMALLOC; } From 149cc025ce3c941ca27a3530497ea3146b6d669b Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Sat, 6 May 2023 14:38:14 +0200 Subject: [PATCH 41/62] style --- client/src/cmdnfc.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/client/src/cmdnfc.c b/client/src/cmdnfc.c index 221b33020..eadfa7161 100644 --- a/client/src/cmdnfc.c +++ b/client/src/cmdnfc.c @@ -118,8 +118,6 @@ static int CmdNfcDecode(const char *Cmd) { PrintAndLogEx(SUCCESS, "MFC dump file detected. Converting..."); uint8_t ndef[4096] = {0}; uint16_t ndeflen = 0; - // uint8_t skip = (4 * MFBLOCK_SIZE); - // convert_mfc_2_arr(dump + skip, bytes_read - skip, ndef, &ndeflen); if (convert_mad_to_arr(dump, bytes_read, ndef, &ndeflen) != PM3_SUCCESS) { PrintAndLogEx(FAILED, "Failed converting, aborting..."); From b52dfa381bc279e73bc91381a5324c302dafa56e Mon Sep 17 00:00:00 2001 From: Christian Herrmann Date: Mon, 8 May 2023 20:03:31 +0200 Subject: [PATCH 42/62] fix output when printing 0,1 numbers like in nested, view commands. Bonus, added NDEF key detection to give hint --- client/src/cmdhfmf.c | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/client/src/cmdhfmf.c b/client/src/cmdhfmf.c index 87c253525..559057459 100644 --- a/client/src/cmdhfmf.c +++ b/client/src/cmdhfmf.c @@ -3882,6 +3882,8 @@ void printKeyTableEx(uint8_t sectorscnt, sector_t *e_sector, uint8_t start_secto PrintAndLogEx(SUCCESS, " Sec | Blk | key A |res| key B |res"); PrintAndLogEx(SUCCESS, "-----+-----+--------------+---+--------------+----"); + uint64_t ndef_key = 0xD3F7D3F7D3F7; + bool has_ndef_key = false; bool extended_legend = false; for (uint8_t i = 0; i < sectorscnt; i++) { @@ -3889,20 +3891,32 @@ void printKeyTableEx(uint8_t sectorscnt, sector_t *e_sector, uint8_t start_secto extended_legend = true; } + if (e_sector[i].Key[0] == ndef_key || e_sector[i].Key[1] == ndef_key) { + has_ndef_key = true; + } + if (e_sector[i].foundKey[0]) { snprintf(strA, sizeof(strA), _GREEN_("%012" PRIX64), e_sector[i].Key[0]); - snprintf(resA, sizeof(resA), _BRIGHT_GREEN_("%c"), e_sector[i].foundKey[0]); + if (extended_legend) { + snprintf(resA, sizeof(resA), _BRIGHT_GREEN_("%c"), e_sector[i].foundKey[0]); + } else { + snprintf(resA, sizeof(resA), _BRIGHT_GREEN_("%d"), e_sector[i].foundKey[0]); + } } else { snprintf(strA, sizeof(strA), _RED_("%s"), "------------"); - snprintf(resA, sizeof(resA), _RED_("%d"), 0); + snprintf(resA, sizeof(resA), _RED_("0")); } if (e_sector[i].foundKey[1]) { snprintf(strB, sizeof(strB), _GREEN_("%012" PRIX64), e_sector[i].Key[1]); - snprintf(resB, sizeof(resB), _BRIGHT_GREEN_("%c"), e_sector[i].foundKey[1]); + if (extended_legend) { + snprintf(resB, sizeof(resB), _BRIGHT_GREEN_("%c"), e_sector[i].foundKey[1]); + } else { + snprintf(resB, sizeof(resB), _BRIGHT_GREEN_("%d"), e_sector[i].foundKey[1]); + } } else { snprintf(strB, sizeof(strB), _RED_("%s"), "------------"); - snprintf(resB, sizeof(resB), _RED_("%d"), 0); + snprintf(resB, sizeof(resB), _RED_("0")); } // keep track if we use start_sector or i @@ -3939,9 +3953,13 @@ void printKeyTableEx(uint8_t sectorscnt, sector_t *e_sector, uint8_t start_secto } // MAD detection - if (e_sector[MF_MAD1_SECTOR].foundKey[0] && e_sector[MF_MAD1_SECTOR].Key[MF_KEY_A] == 0xA0A1A2A3A4A5) { + if (e_sector[MF_MAD1_SECTOR].foundKey[0] && e_sector[MF_MAD1_SECTOR].Key[0] == 0xA0A1A2A3A4A5) { PrintAndLogEx(HINT, "MAD key detected. Try " _YELLOW_("`hf mf mad`") " for more details"); } + // NDEF detection + if (has_ndef_key) { + PrintAndLogEx(HINT, "NDEF key detected. Try " _YELLOW_("`hf mf ndefread`") " for more details"); + } PrintAndLogEx(NORMAL, ""); } From fcc922259fb2bd5628d1337990eea20ddea9a3ff Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Mon, 8 May 2023 20:03:31 +0200 Subject: [PATCH 43/62] fix output when printing 0,1 numbers like in nested, view commands. Bonus, added NDEF key detection to give hint --- client/src/cmdhfmf.c | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/client/src/cmdhfmf.c b/client/src/cmdhfmf.c index 87c253525..559057459 100644 --- a/client/src/cmdhfmf.c +++ b/client/src/cmdhfmf.c @@ -3882,6 +3882,8 @@ void printKeyTableEx(uint8_t sectorscnt, sector_t *e_sector, uint8_t start_secto PrintAndLogEx(SUCCESS, " Sec | Blk | key A |res| key B |res"); PrintAndLogEx(SUCCESS, "-----+-----+--------------+---+--------------+----"); + uint64_t ndef_key = 0xD3F7D3F7D3F7; + bool has_ndef_key = false; bool extended_legend = false; for (uint8_t i = 0; i < sectorscnt; i++) { @@ -3889,20 +3891,32 @@ void printKeyTableEx(uint8_t sectorscnt, sector_t *e_sector, uint8_t start_secto extended_legend = true; } + if (e_sector[i].Key[0] == ndef_key || e_sector[i].Key[1] == ndef_key) { + has_ndef_key = true; + } + if (e_sector[i].foundKey[0]) { snprintf(strA, sizeof(strA), _GREEN_("%012" PRIX64), e_sector[i].Key[0]); - snprintf(resA, sizeof(resA), _BRIGHT_GREEN_("%c"), e_sector[i].foundKey[0]); + if (extended_legend) { + snprintf(resA, sizeof(resA), _BRIGHT_GREEN_("%c"), e_sector[i].foundKey[0]); + } else { + snprintf(resA, sizeof(resA), _BRIGHT_GREEN_("%d"), e_sector[i].foundKey[0]); + } } else { snprintf(strA, sizeof(strA), _RED_("%s"), "------------"); - snprintf(resA, sizeof(resA), _RED_("%d"), 0); + snprintf(resA, sizeof(resA), _RED_("0")); } if (e_sector[i].foundKey[1]) { snprintf(strB, sizeof(strB), _GREEN_("%012" PRIX64), e_sector[i].Key[1]); - snprintf(resB, sizeof(resB), _BRIGHT_GREEN_("%c"), e_sector[i].foundKey[1]); + if (extended_legend) { + snprintf(resB, sizeof(resB), _BRIGHT_GREEN_("%c"), e_sector[i].foundKey[1]); + } else { + snprintf(resB, sizeof(resB), _BRIGHT_GREEN_("%d"), e_sector[i].foundKey[1]); + } } else { snprintf(strB, sizeof(strB), _RED_("%s"), "------------"); - snprintf(resB, sizeof(resB), _RED_("%d"), 0); + snprintf(resB, sizeof(resB), _RED_("0")); } // keep track if we use start_sector or i @@ -3939,9 +3953,13 @@ void printKeyTableEx(uint8_t sectorscnt, sector_t *e_sector, uint8_t start_secto } // MAD detection - if (e_sector[MF_MAD1_SECTOR].foundKey[0] && e_sector[MF_MAD1_SECTOR].Key[MF_KEY_A] == 0xA0A1A2A3A4A5) { + if (e_sector[MF_MAD1_SECTOR].foundKey[0] && e_sector[MF_MAD1_SECTOR].Key[0] == 0xA0A1A2A3A4A5) { PrintAndLogEx(HINT, "MAD key detected. Try " _YELLOW_("`hf mf mad`") " for more details"); } + // NDEF detection + if (has_ndef_key) { + PrintAndLogEx(HINT, "NDEF key detected. Try " _YELLOW_("`hf mf ndefread`") " for more details"); + } PrintAndLogEx(NORMAL, ""); } From dadaf9cd11e5eced49d7b97d1398cb1e96e0e334 Mon Sep 17 00:00:00 2001 From: 0xdanneh <126520353+0xdanneh@users.noreply.github.com> Date: Mon, 8 May 2023 20:48:41 +0100 Subject: [PATCH 44/62] 'Fix' writing on hitag2 in password mode Needs more investigation, but it does solve the issue of write commands not working consistently --- armsrc/hitag2.c | 1 + 1 file changed, 1 insertion(+) diff --git a/armsrc/hitag2.c b/armsrc/hitag2.c index f5730f14b..90a667105 100644 --- a/armsrc/hitag2.c +++ b/armsrc/hitag2.c @@ -655,6 +655,7 @@ static bool hitag2_password(uint8_t *rx, const size_t rxlen, uint8_t *tx, size_t *txlen = 0; if (bPwd && (bAuthenticating == false) && write) { + SpinDelay(2); if (hitag2_write_page(rx, rxlen, tx, txlen) == false) { return false; } From eae5da6b229e629eb3a8092a7ff6a1a871547180 Mon Sep 17 00:00:00 2001 From: Cory Solovewicz Date: Thu, 18 May 2023 00:15:55 -0700 Subject: [PATCH 45/62] Update amiibo_tools.lua Update Lua Table with new entries. Signed-off-by: Cory Solovewicz --- client/lualibs/amiibo_tools.lua | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/client/lualibs/amiibo_tools.lua b/client/lualibs/amiibo_tools.lua index b4c3f3bbf..e5838ebef 100644 --- a/client/lualibs/amiibo_tools.lua +++ b/client/lualibs/amiibo_tools.lua @@ -55,6 +55,9 @@ amiibo_tools.db = ["0x00000003039bff02"] = { name = "Mario - Power Up Band" }, + ["0x000000030430ff02"] = { + name = "Golden - Power Up Band" + }, ["0x0000010000190002"] = { name = "Dr. Mario" }, @@ -214,6 +217,9 @@ amiibo_tools.db = ["0x0100000003990902"] = { name = "Link - Link's Awakening" }, + ["0x0100000004180902"] = { + name = "Link - Tears of the Kingdom" + }, ["0x0100010000160002"] = { name = "Toon Link" }, @@ -1834,6 +1840,9 @@ amiibo_tools.db = ["0x0800010003820002"] = { name = "Inkling" }, + ["0x0800010004150402"] = { + name = "Inkling - Yellow" + }, ["0x08000200003f0402"] = { name = "Inkling Boy" }, @@ -1870,9 +1879,15 @@ amiibo_tools.db = ["0x08050200038f0402"] = { name = "Octoling Boy" }, + ["0x08050200041b0402"] = { + name = "Octoling - Blue" + }, ["0x0805030003900402"] = { name = "Octoling Octopus" }, + ["0x08060100041c0402"] = { + name = "Smallfry" + }, ["0x09c0010102690e02"] = { name = "Mario - Soccer" }, @@ -2374,6 +2389,9 @@ amiibo_tools.db = ["0x3380000003781402"] = { name = "Solaire of Astora" }, + ["0x33c0000004200002"] = { + name = "Kazuya" + }, ["0x3480000000310002"] = { name = "Mega Man" }, @@ -2455,6 +2473,9 @@ amiibo_tools.db = ["0x3600010003620002"] = { name = "Cloud - Player 2" }, + ["0x3601000004210002"] = { + name = "Sephiroth" + }, ["0x3640000003a20002"] = { name = "Hero" }, @@ -2520,6 +2541,12 @@ amiibo_tools.db = }, ["0x3c80000003a40002"] = { name = "Terry" + }, + ["0x3dc0000004220002"] = { + name = "Steve" + }, + ["0x3dc1000004230002"] = { + name = "Alex" } }, game_series = { @@ -2621,6 +2648,7 @@ amiibo_tools.db = ["0x324"] = "Bayonetta", ["0x334"] = "Pac-man", ["0x338"] = "Dark Souls", + ["0x33c"] = "Tekken", ["0x348"] = "Megaman", ["0x34c"] = "Street fighter", ["0x350"] = "Monster Hunter", @@ -2635,7 +2663,8 @@ amiibo_tools.db = ["0x38c"] = "Diablo", ["0x3a0"] = "Persona", ["0x3b4"] = "Banjo Kazooie", - ["0x3c8"] = "Fatal Fury" + ["0x3c8"] = "Fatal Fury", + ["0x3dc"] = "Minecraft" }, types = { ["0x00"] = "Figure", From 3939bcb541c95c86428cbd1c0af170bab0ee914f Mon Sep 17 00:00:00 2001 From: Cory Solovewicz Date: Thu, 18 May 2023 09:35:09 -0700 Subject: [PATCH 46/62] Create update_amiibo_tools_lua.py This is a python script to automate what the updating of the amiibo_tools.lua file which holds a lua table of all known amiibos. Previously updating the amiibo_tools.lua was a very manual process and prone to errors. This simplifies the process. Signed-off-by: Cory Solovewicz --- client/update_amiibo_tools_lua.py | 114 ++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 client/update_amiibo_tools_lua.py diff --git a/client/update_amiibo_tools_lua.py b/client/update_amiibo_tools_lua.py new file mode 100644 index 000000000..873a6e18d --- /dev/null +++ b/client/update_amiibo_tools_lua.py @@ -0,0 +1,114 @@ +#!/usr/bin/env python3 + +""" +----------------------------------------------------------------------------- +Name: update_amiibo_tools_lua.py + +Author: Cory Solovewicz + +Description: +This is a python script to automate what the updating of the amiibo_tools.lua +file which holds a lua table of all known amiibos. Previously updating the +amiibo_tools.lua was a very manual process. + +This script automates the following original command: +curl https://raw.githubusercontent.com/N3evin/AmiiboAPI/master/database/amiibo.json | jq 'del(.amiibos[].release)' | jq 'del(.characters)' | pbcopy --> transform to table +And outputs the formatted file as amiibo_tools.lua +If everything goes well, this should be an updated copy of amiibo_tools.lua +which can then be placed in the /lualibs/ directory. +The temporary amiibo.json file is then deleted + +Dependencies: +python3 -m pip install jq + +How to run: +python update_amiibo_tools_lua.py +The script will create the file amiibo_tools.lua + +After running, manually backup the original /lualibs/amiibo_tools.lua and move the +updated amiibo_tools.lua to the /lualibs/ directory. +----------------------------------------------------------------------------- +""" + +import os +import re +import subprocess +import json +from jq import jq + +def fetch_data(): + print("Fetching amiibo.json") + # Perform the curl command + curl_command = "curl https://raw.githubusercontent.com/N3evin/AmiiboAPI/master/database/amiibo.json" + curl_process = subprocess.Popen(curl_command.split(), stdout=subprocess.PIPE, stderr=subprocess.PIPE) + output, error = curl_process.communicate() + + if curl_process.returncode != 0: + print("Error fetching data: ", error.decode()) + return None + return output + +def filter_data(data): + print("Filtering downloaded data") + # Convert the output to JSON and use jq to filter data + data_json = json.loads(data) + filtered_data_json = jq('del(.amiibos[].release) | del(.characters)').transform(data_json) + # Convert the filtered JSON data back to a string, preserving Unicode characters + filtered_data = json.dumps(filtered_data_json, indent=2, ensure_ascii=False) + return filtered_data + +def save_data(filtered_data, filename): + # Save filtered data to file + with open(filename, 'w', encoding='utf-8') as file: + file.write(filtered_data) + print(f"Data saved to {filename}") + +def process_file(filename): + # Open the file + with open(filename, 'r', encoding='utf-8') as file: + data = file.read() + + # Perform the replacements + data = data.replace('"name"', 'name') + data = data.replace('"amiibo_series"', 'amiibo_series') + data = data.replace('"amiibos"', 'amiibos') + data = data.replace('"game_series"', 'game_series') + data = data.replace('"types"', 'types') + data = data.replace(':', ' =') + data = re.sub('"0x', '["0x', data) + data = re.sub('" =', '"] =', data) + + # Prepend the text + prepend_text = 'local amiibo_tools = {}\n\n-- curl https://raw.githubusercontent.com/N3evin/AmiiboAPI/master/database/amiibo.json | jq \'del(.amiibos[].release)\' | jq \'del(.characters)\' | pbcopy --> transform to table\namiibo_tools.db =\n' + data = prepend_text + data + + # Append the text + append_text = '\n\nreturn amiibo_tools\n' + data = data + append_text + + return data + +def write_to_file(data, filename): + # Write the output + with open(filename, 'w', encoding='utf-8') as file: + file.write(data) + print(f"Output written to {filename}") + +def delete_file(filename): + try: + os.remove(filename) + print(f"Temporary file {filename} deleted") + except OSError as e: + print(f"Error deleting file {filename}: ", e) + +def main(): + data = fetch_data() + if data: + filtered_data = filter_data(data) + save_data(filtered_data, 'amiibo.json') + processed_data = process_file('amiibo.json') + write_to_file(processed_data, 'amiibo_tools.lua') + delete_file('amiibo.json') + +if __name__ == "__main__": + main() From 6f8a4eb79aa7beab21762872e40fad641dfed23d Mon Sep 17 00:00:00 2001 From: Cory Solovewicz Date: Thu, 18 May 2023 09:43:06 -0700 Subject: [PATCH 47/62] Update CHANGELOG.md Add description of changes to the change log. Signed-off-by: Cory Solovewicz --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index dff4bcf59..41c051541 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -61,6 +61,7 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac - Update documentation for installation on macOS with MacPorts (@linuxgemini) - Added possible Paxton id to hitag2 tag info output - Changed `hf mf sim` - reduce 50ms threshold to 6ms for reset to idle #1974 (@net147) + - Update `amiibo_tools.lua` with new identifiers and create a python script `update_amiibo_tools_lua.py` to automate the process in the future. (@CorySolovewicz) ## [Nitride.4.16191][2023-01-29] - Changed `build_all_firmwares.sh` to fit GENERIC 256kb firmware images (@doegox) From f9deb75b3d3e9f5f84a41e85c534cd77c1a69131 Mon Sep 17 00:00:00 2001 From: OscarAkaElvis Date: Sat, 20 May 2023 00:00:55 +0200 Subject: [PATCH 48/62] Add python3 script to convert amiibo nfc Flipper Zero files to eml files to be used with Proxmark3 --- client/pyscripts/pm3_nfc2eml.py | 112 ++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100755 client/pyscripts/pm3_nfc2eml.py diff --git a/client/pyscripts/pm3_nfc2eml.py b/client/pyscripts/pm3_nfc2eml.py new file mode 100755 index 000000000..e47ff8464 --- /dev/null +++ b/client/pyscripts/pm3_nfc2eml.py @@ -0,0 +1,112 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +""" +Script to convert amiibo nfc Flipper Zero files to eml files to be used with Proxmark3 +Date: 18/05/2023 +Script Author: OscarAkaElvis +Tested on: Linux and Windows +OscarAkaElvis - https://twitter.com/OscarAkaElvis +""" + +import re +import argparse +import os +from os import path +from sys import argv + +# Vars +script_name = path.basename(argv[0]) +script_version = "1.0" + +# Function to print script's version +def print_version(): + print() + return 'v{}'.format(script_version) + +# Check if the input file is a text file based on its content +def is_text_file(file_path): + with open(file_path, 'rb') as file: + try: + file_content = file.read().decode('utf-8') + return all(ord(char) < 128 for char in file_content) + except UnicodeDecodeError: + return False + +# Main script code +def main(): + # Text help data + description_text = "Script to convert amiibo nfc Flipper Zero files to eml files to be used with Proxmark3." + epilog_text = 'Example:\n python3 ' + script_name + ' -i file.nfc -o output.eml' + + # Create an argument parser + parser = argparse.ArgumentParser(exit_on_error=False, description=description_text, formatter_class=argparse.RawDescriptionHelpFormatter, epilog=epilog_text) + # Add the input file argument + parser.add_argument("-i", "--input", required=True, help="Path to the input nfc file.") + # Add the output file argument + parser.add_argument("-o", "--output", required=True, help="Name of the output eml file.") + # Add the version argument + parser.add_argument('-v', '--version', action='version', version=print_version(), help="Show script's version number and exit") + + # Parse the command-line arguments + + args = parser.parse_args() + + # Extract the file paths from the command-line arguments + file_path = args.input + output_file = args.output + + # Check if the target file exists + if not os.path.isfile(file_path): + print(f"[!] The input file '{file_path}' does not exist.") + exit(0) + + # Validation to check if the input file is a text file based on its content + if not is_text_file(file_path): + print("[!] The input file does not appear to be a nfc text file.") + + # Check if the input file name has the .nfc extension + if not file_path.lower().endswith('.nfc'): + print("[+] Warning. The input file should have the '.nfc' extension.") + + # Get the absolute path of the output file + output_file_path = os.path.abspath(output_file) + + # Check if the directory of the output file is writable + output_dir = os.path.dirname(output_file_path) + if not os.access(output_dir, os.W_OK): + print(f"[!] The output directory '{output_dir}' is not writable or doesn't exist.") + + # Check if the output file name has the .eml extension + if not output_file.lower().endswith('.eml'): + print("[+] Warning. The output file should have the '.eml' extension.") + + # Read the target file + with open(file_path, 'r') as file: + file_content = file.read() + + # Extract the data from each "Page X" line + matches = re.findall(r'Page \d+:(.*?)$', file_content, re.MULTILINE | re.DOTALL) + + if matches: + # Remove spaces and convert to lowercase for each match + extracted_data = [re.sub(r'\s', '', match).lower() for match in matches] + + # Join the extracted data with line feeds + result = '\n'.join(extracted_data) + + # Write the extracted data to the output file + with open(output_file_path, 'w', newline='\n') as output_file: + output_file.write(result) + print(f"[*] File converted successfully. Output eml file written as '{os.path.basename(output_file.name)}'.") + else: + # If the needed data is not there + print("[!] Provided input file seems to not be a valid nfc file to work with.") + +# Application entrypoint +if __name__ == '__main__': + try: + main() + except: + pass + From 5132900d9c20ed6f1241b3990f08f6c2518d0e0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93scar=20Alfonso=20D=C3=ADaz?= Date: Sat, 20 May 2023 00:05:55 +0200 Subject: [PATCH 49/62] Update CHANGELOG.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Óscar Alfonso Díaz --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index dff4bcf59..9694529ce 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 python3 script to convert amiibo nfc Flipper Zero files to eml files to be used with Proxmark3 (@OscarAkaElvis) - Changed `nfc decode -f` - now can detect and convert MFC dumpfiles to NDEF byte arrays (@iceman1001) - Changed `nfc decode` - now handles EXTERNAL RECORDS better (@iceman1001) - Fixed `nfc decode` - now handles NDEF Signature version1 records better (@iceman1001) From ca6e9e4ba948b834f14db3e3461ec044541e2af9 Mon Sep 17 00:00:00 2001 From: Self Not Found Date: Sun, 21 May 2023 10:07:17 +0800 Subject: [PATCH 50/62] Auth both key A and key B in hf mf restore --- client/src/cmdhfmf.c | 45 +++++++++++++------------------------------- 1 file changed, 13 insertions(+), 32 deletions(-) diff --git a/client/src/cmdhfmf.c b/client/src/cmdhfmf.c index 559057459..8f39ca3e8 100644 --- a/client/src/cmdhfmf.c +++ b/client/src/cmdhfmf.c @@ -1109,7 +1109,7 @@ static int CmdHF14AMfRestore(const char *Cmd) { "Restore MIFARE Classic dump file to tag.\n" "\n" "The key file and dump file will program the card sector trailers.\n" - "By default we authenticate to card with key B 0xFFFFFFFFFFFF.\n" + "By default we authenticate to card with key 0xFFFFFFFFFFFF.\n" "If access rights in dump file is all zeros, it will be replaced with default values\n" "\n" "`--uid` param is used for filename templates `hf-mf--dump.bin` and `hf-mf--key.bin.\n" @@ -1321,56 +1321,37 @@ static int CmdHF14AMfRestore(const char *Cmd) { uint8_t wdata[26]; memcpy(wdata + 10, bldata, sizeof(bldata)); - if (use_keyfile_for_auth) { - for (int8_t kt = MF_KEY_B; kt > -1; kt--) { - + for (int8_t kt = MF_KEY_B; kt > -1; kt--) { + if (use_keyfile_for_auth) { if (kt == MF_KEY_A) memcpy(wdata, keyA[s], 6); else memcpy(wdata, keyB[s], 6); - - PrintAndLogEx(INFO, "block %3d: %s", mfFirstBlockOfSector(s) + b, sprint_hex(bldata, sizeof(bldata))); - - clearCommandBuffer(); - SendCommandMIX(CMD_HF_MIFARE_WRITEBL, mfFirstBlockOfSector(s) + b, kt, 0, wdata, sizeof(wdata)); - PacketResponseNG resp; - if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { - uint8_t isOK = resp.oldarg[0] & 0xff; - if (isOK == 0) { - if (b == 0) { - PrintAndLogEx(INFO, "Writing to manufacture block w key %c ( " _RED_("fail") " )", (kt == MF_KEY_A) ? 'A' : 'B'); - } else { - PrintAndLogEx(FAILED, "Write to block %u w key %c ( " _RED_("fail") " ) ", b, (kt == MF_KEY_A) ? 'A' : 'B'); - } - } else { - // if success, skip to next block - break; - } - } else { - PrintAndLogEx(WARNING, "Command execute timeout"); - } + } else { + // use default key to authenticate for the write command + memcpy(wdata, default_key, 6); } - } else { - // use default key to authenticate for the write command - memcpy(wdata, default_key, 6); PrintAndLogEx(INFO, "block %3d: %s", mfFirstBlockOfSector(s) + b, sprint_hex(bldata, sizeof(bldata))); clearCommandBuffer(); - SendCommandMIX(CMD_HF_MIFARE_WRITEBL, mfFirstBlockOfSector(s) + b, MF_KEY_B, 0, wdata, sizeof(wdata)); + SendCommandMIX(CMD_HF_MIFARE_WRITEBL, mfFirstBlockOfSector(s) + b, kt, 0, wdata, sizeof(wdata)); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { uint8_t isOK = resp.oldarg[0] & 0xff; if (isOK == 0) { if (b == 0) { - PrintAndLogEx(INFO, "Writing to manufacture block w key B ( " _RED_("fail") " )"); + PrintAndLogEx(INFO, "Writing to manufacture block w key %c ( " _RED_("fail") " )", (kt == MF_KEY_A) ? 'A' : 'B'); } else { - PrintAndLogEx(FAILED, "Write to block %u w key B ( " _RED_("fail") " )", b); + PrintAndLogEx(FAILED, "Write to block %u w key %c ( " _RED_("fail") " ) ", b, (kt == MF_KEY_A) ? 'A' : 'B'); } + } else { + // if success, skip to next block + break; } } else { PrintAndLogEx(WARNING, "Command execute timeout"); } - } // end use_keyfile_for_auth + } } // end loop B } // end loop S From 98c0046bdb12c780107f90307818e4f5f2b78a82 Mon Sep 17 00:00:00 2001 From: Self Not Found Date: Sun, 21 May 2023 10:51:10 +0800 Subject: [PATCH 51/62] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index dff4bcf59..f88ee7ef1 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 mf restore` - Auth both key A and key B with default password (@wh201906) - Changed `nfc decode -f` - now can detect and convert MFC dumpfiles to NDEF byte arrays (@iceman1001) - Changed `nfc decode` - now handles EXTERNAL RECORDS better (@iceman1001) - Fixed `nfc decode` - now handles NDEF Signature version1 records better (@iceman1001) From 03e1b23fb02157e2cd385e5cdb252413c0b910ec Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Tue, 23 May 2023 13:05:29 +0200 Subject: [PATCH 52/62] fixes #1986. spelling --- client/src/cmdhfmfu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/src/cmdhfmfu.c b/client/src/cmdhfmfu.c index e47a60e7c..f720d8599 100644 --- a/client/src/cmdhfmfu.c +++ b/client/src/cmdhfmfu.c @@ -2690,7 +2690,7 @@ static void wait4response(uint8_t b) { int CmdHF14MfUTamper(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mfu tamper", - "Set the congiguration of the NTAG 213TT tamper feature\n" + "Set the configuration of the NTAG 213TT tamper feature\n" "Supports:\n" "NTAG 213TT\n", "hf mfu tamper -e -> enable tamper feature\n" @@ -4697,7 +4697,7 @@ static command_t CommandTable[] = { {"restore", CmdHF14AMfURestore, IfPm3Iso14443a, "Restore a dump onto a MFU MAGIC tag"}, {"view", CmdHF14AMfuView, AlwaysAvailable, "Display content from tag dump file"}, {"wrbl", CmdHF14AMfUWrBl, IfPm3Iso14443a, "Write block"}, - {"tamper", CmdHF14MfUTamper, IfPm3Iso14443a, "Cofigure the tamper feature on an NTAG 213TT"}, + {"tamper", CmdHF14MfUTamper, IfPm3Iso14443a, "Configure the tamper feature on an NTAG 213TT"}, {"---------", CmdHelp, IfPm3Iso14443a, "----------------------- " _CYAN_("simulation") " -----------------------"}, {"eload", CmdHF14AMfUeLoad, IfPm3Iso14443a, "Load Ultralight dump file into emulator memory"}, {"esave", CmdHF14AMfuESave, IfPm3Iso14443a, "Save Ultralight dump file from emulator memory"}, From c10fbacd2023dd4b082c1033041e9c24de1d67cc Mon Sep 17 00:00:00 2001 From: d18c7db Date: Tue, 23 May 2023 14:26:20 +0200 Subject: [PATCH 53/62] Synchronized minor FPGA orphaned change In file hi_iso14443a.v from fpga-xc3s100e to fpga-xc2s30 and rebuilt the FPGA bitstream files for the Spartan-2 --- CHANGELOG.md | 2 ++ fpga-xc2s30/fpga_felica.bit | Bin 42179 -> 42172 bytes fpga-xc2s30/fpga_hf.bit | Bin 42175 -> 42168 bytes fpga-xc2s30/fpga_hf_15.bit | Bin 42178 -> 42171 bytes fpga-xc2s30/fpga_lf.bit | Bin 42175 -> 42168 bytes fpga-xc2s30/hi_iso14443a.v | 24 ++++++++++++++++++------ 6 files changed, 20 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 73db91565..5783b0f46 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ 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] + - Rebuilt the Spartan-2 `fpga_*.bit` files to include the `hi_iso14443a.v` update (@d18c7db) + - Added minor orphaned change from `hi_iso14443a.v` in `fpga-xc3s100e` to `hi_iso14443a.v` in `fpga-xc2s30` (@d18c7db) - Added python3 script to convert amiibo nfc Flipper Zero files to eml files to be used with Proxmark3 (@OscarAkaElvis) - Changed `hf mf restore` - Auth both key A and key B with default password (@wh201906) - Changed `nfc decode -f` - now can detect and convert MFC dumpfiles to NDEF byte arrays (@iceman1001) diff --git a/fpga-xc2s30/fpga_felica.bit b/fpga-xc2s30/fpga_felica.bit index d87b879de72ddc013ddb7da919f9f255421a835d..83009b499879dedcd6b741fb39cd016f915c9107 100644 GIT binary patch literal 42172 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

4u?&%}qr#jTc&F>9hiLT#NQhiOD-%ZGRs!(` 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 diff --git a/fpga-xc2s30/fpga_hf.bit b/fpga-xc2s30/fpga_hf.bit index ce658c702206a40d0feb7f89a4aae0eb9db38739..06a4569f93098a4631f0a7f2e2f2eed4d93e32da 100644 GIT binary patch literal 42168 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 E0pOhEnE(I) diff --git a/fpga-xc2s30/fpga_hf_15.bit b/fpga-xc2s30/fpga_hf_15.bit index 30db5253b0bfc0a846cf6bfd9722ee50293b32b1..0e59dd3d0f48262003e37efb22b43b55b0e97372 100644 GIT binary patch literal 42171 zcma&Pe|S{onKyo)=bVI-Ig`vG1m6@DPbPs5>0~k_h%to8Lz1`eR8Uga?_O-Les@P? zZQ7Uarmoj}(QdDPpG+bQ1Y|(8)R*lZBD&Gib_`S_SbD(J9Wcd_vM##Rju%Kqt=X2lBBbE3ja{nVz?kC@a-Cw)^Z@M1*o5i<> z{_cTww3a*#|IirzU*B3B4$}i%+I#H#E{ZDp|aw8UJs*o&J_+U-u1sp8LW7 z^I({SLPWE|N=*J!4=a{JqO~qej{J9;`ty=7;rqhBD`Dc)Ni`2pxhcQ?KYTRd{=t8t zjl%v9ugHC!d(VHRP5J-rJzDNP|CKg#{;%(G|M0p=f?`xj#iUF1%QlIklk}7lJX&HhFZEIl*TZZ*B`N6W7F(;h?{Vh-g!-x0)NQh8ko*ach{vqm z6mm+E@op_9-$Qdx(nngoQ<`Nh;utMSdAF($&>3ot{l9%`r{=yVs|;&5C{pgr81K?= zsD6d-ih3mWdx|_*7Hc2Ii1Fd(etL}>GG3wX7Z-T5<2~5>yXd6Z(y{evHKSRwO}Z#h zxgA|WSBbWepLC7ALc^V*PW@^2F>%=@Tg0jNe#_LuclFToWM%a`*(jw%FsAEl5?c%( zOSz+Mlc>rDqU?vX(W$a1T7HUN5DQ}Z8kWwzC#sY63U+JIR0W;mO;2j1I^GHTE+4gK zox<16j|af4hpJxr0Wq2bal zIy2Z%TehWtz#gX|3JO*BIZ_+k(i>7cWt-i%o;I(bhown%jDAfK$Cp$)=nA!RUq(G& z+%4Nw7G1s5>qR5z>Vi(A({mqtgzC8`1S9kov=)gP@OP3%XgVCQk|oB zv+=3-=<}`1B5Z}a)Oqx3yw37%XxvHni5ANnRX=2MDM-K7PCkz%D6`e|=BE@9zOcHE z@Zk7TYP>im+wh|6d{iv6%F^mXG$I-;Ut0ZyekEFC-ZQF`Td%>{yZ9sMkJZ5{=n<_d zY4FAj|HP~l`u*&~+&mK{SN55UsMcap`XN;n8|&F+>k++<2RfOPv#(gianVCHowR`6 zv*NLYox0g-dxZu>Q0O%*om=<8xi+k+c{udA8e?Z-4cJI9^DQDGyw9mqza{8*7{#Hq zucCdQb%7%3+mh}3vBx6Zw^PaH>`O5x=;NbH6Tu0!lJ23~c93S&Uq(P1PyMV&2yB9 z`(yfkh6fRf=?QjG{32GJ(I?bcZk`jcuO211qNJ*P45%3tCB#Oo13J84m<=GDz{i=p zPv1&uer|nrtxbleME-{(-Nd{v!%@|j2b$sH#puM{btU-(nUeN zmj|IJZUQ;b+ za(am0x{E%e$U8A)(%mX)zj>+ zNusdc#1IV1--=d@dV7<>orplnv|eF3(Eux>{-osu7PIo)x{uF&4*h9Odb61McdSM` znCs59<8Sd~Vl?S7jdQ$7W zPpa_zc0(*U1HT?D-t73h13TCU;w9VPiDeq2mz;)}UIA-z?W?k=Mr_lo+hAXSwsL=a zWwH%zxIe1zVtM?6+mZgzEFY0E32!pl2!GzRR_U|L^Z0d_ogO?;>wPXBqe%dnCaN)t z@gdXuXn!8R;NGQ|$jrH{eI{Y6v;9@^nccMB_jUolF4M1A0|#1-(Q)ANwxoJl|BM!M z?^(4t=MS6A@p6PuiZ0**w!oDGwz4#gov9~0WtuxKz34Q9t910v@@we)vnxuvncMjj z7(1KBFKJ&ceqBy%Q~u{GfUPk)On#2pD9){gJ@nt$(`W^q_p-qELsl@ZdNg?9rL`yBmck0)ne zwo>VC$v(cT@x=_zBAQtYOnV+ zX1!Y4fj3xfZk~Z(WK-2KAnaLy?}8OZH@hrR zuS4%dZ`}E@Dn=0&T9rU*2e57>4W(@(?Iddjc7mR>ufe&4@blPpFd6fTwUm4bb{;HX z$nmC(soxUtYnQ0S@g2l)K9&zDb&SJHI2d3yXBUe3F8zuc2zL5047M>IJoXoVGLO>p zJa}294%a{TXb)0?`5jM^U6dxn^^|`EAm#2To4fl^6a_6wVe}{1@5EsT4=FV6C!m8w zG!C}`8laI7w&k%J972J>kOEcE<&>dI1la$FHmAMfMNm?N(o=f2~@UF3YIp2s{?cxTQO1U&8#5Halxm zfo;{*ERporpsOKrM=@NU=U?Mw&$egl3b0AQG`nS&ow?6yo5?Tq z6x9S06@Ia0cRWbXN#gT7>&~q^pL>BmG6y<>r1Z6=W6h}_4y%jM1~b34z`qW4ekvka z-^44=neg*>fjnLY5rdzfRP+36Fa5eI%(3oOn3fmJ;BfmQ0Y>I{`>ON!rR)8%wdrVT zpq}?WWS`Ras~s99zCv$`=ke=HVkg;015HdYq10H+EbmaPv4E*Jvpjw^=RAM3_=mAg zJ8H(*ciDHUeku$J3VHm}OHS2Tj+YEU4lOZ@lST*jROK;mSjpqpZieBE_$I)=ZqTx9 z=_SM)?;xl^yphMRH2o2~H$hLfS1-Veox&a%GJZ2vc`V>aSLYX<*{j#ow)Mv6YGe0J6ii zp|W0^CK1-4&w2d1re9&MpC5z6U&8NxTV0n!GS#1c`{?K7>o78T{x$bidW$5_RV5Ki@vMh^RJ>s9Jnm>z?J~plBgEj*0Du& zh=-EKK6WDK>jFiR+FOp^R1Dn)o6TruJyt5Zo>pq{DgI?EEd&{XLq;&=0|5*65ct!HJ*_d{4F0tz*Pocjq>boLAm*uzy`a_gA>hpOuRVN|3F)H~ zea6CBI*;FC=GH4d#lNhIlcs1RjVxAL3D`QNTWtyrS;3?yz!I8^DT8yjS+B8hcHds3 zUYs%;#)G%3ettC?*=IaG&Go>)#JCLOA6K*bg)c`4%OqKUqyrCm{&kIBSMCsATMdJD zVH*z`O>{EZXnQZ@`4_DxIBN=+{p;x^-S5DxH0lwS6gsB)S5XjXwGdvN1rcJQ%??&$ z_G|uM>Z!JA{-p(H3udiIgc(;30KW`TuH(&?^c%+%2?fBf_0Y$b3ixLWAq4s(Kr~$c zoKo}r>x}2JWLtUu&Dy5K0z0sV zm75!nR%MMS^YHKY)MXJdMsx3p>N*tfrD^Dg5Gk#2r%yio8b;&|tM@Yb8t|`Scz|W1 z{te9hbp<9P7w!+h<@yOVSsasX28$eeMMzx!3(Y(pZ*Y9W*Z{e4wde6GDPfDI?cFgf zkIptx-I>4){cyZfQ#qVW zZtQvd8U)*F5Z=8WNxQIKS+xldz%P3SezD262(1<>AV6+N0YifpN4}mJ_{HA>ms#~B zX!3CeV@#^Q#0F@IfruT;?J=P}C^pAJJYcrf0Hi{l2Dp`cSQ|*PsW`I|_VrKj>W~d9 zq=wc8{2HUpeSa54(DiC=Ep)8{k#Jo{^bO`daA;Og4@B@TC>~SxHC%c>RO^W8)zm6D zYVvkaW)XU2=&S`0^E6&V88zeqCmgfBAN(H2{tj#>Fr| z(YRuDraI5RvVbki4@H{oOGvvAEX)$zPb&qOZ&OT)F0pH<>-$ zmYpx~FG76CyC5xM?A6@J6fbeVfg{TEuMs-R8=xP~sbR;doUh(xe4a~U2(+5XzmiAL zMs;Cp9fZVJ5j9~^b{cv78lZ8YRhcs<#YXj3@;bd?EN+A_KY9FWq9aNu3H~0S_5Yt$dYk1e;o8;rl?my^r0-VFu)a1hiZLiM&PsUn6W)C=0A<`R}X zeqE(siAKvi-X0;OQDWXS2+PT2Yt}odQqCU^&fY;A!G!}E&*F}MA;^Xgm!AcY)nx+T zQooUl!=XUaBWAUxbC1;U>+rzNP#>!wfepfC$ThVpvJ}s$PnPu*1ImaMVrkcp_2&5(=%g0WeWD#gVhpsaMg3QOl1Dm0#PawxNhje+ z?#ZY_ViYL}Z@SmAUbmM!-ibVZX%DbOww?mW-LU?O&=A~vo!QSr_rpKu@vD|L_beoj zU2apq9umN`QmeM`KuwD1HH3dCt z%0K7vD}!)%x#POQYua+!?!YnL(3U%Y-R9JxlT6_Z}`sjO5)sV`Jw=_ zZaL46`Flz@K$+q4-)jfRf6Um=ey<-Ep)3GsihrexE>heKq|w7sh@V#4urSMr&y<_3 z;ynM_Pg|_#(w?E>i)iD2b*0n~p|47y59ijM&%KCk+vEg~s91~k@(G`OTrA=zk{8Vc=iutM0 z5NkPb__ew9O3y}os8l)8ZuP5x`6|a)%PO@OlsYnE)B2&Hoz|nGI;(Gp|Fia@xIb&u z`sWk+oHaJEyncAh>J|QQK=V5^%>5NuQ_@o$NeU|Hhll8-)9}oyA6NI&)!jn}ebFxj z;3Y!BXV1#(huPxE@H12e?6bU%)R+tzhE}_!a2y1OFPP zdc=s+`L8JA!yd)Yfv_WH4_Q%AstRyM^jTuA`<}|V`&hps`RT?n>ec*hMkhN+Key_+ zv9rLx&RfT=hRz_eZym>>!Gd-zq$>ist=;Mr|LRiqMK9Se|ATL*dS1V>=ZNmxrJfg8 z#P>Kxn#ZrbG^sa0*J{TDZ#I!9g5T(Ctim^W{ZKcLj8rFed+j70v8pY@ZX2gV3L@BS z`(3$MLh`R6ZrS?hSf7QYiM^8AoFbVKi#D*les~KVfexv6v(unm$TxLDl>3F;z2mGa z_a5*s`jdFx4sK}wA^g~)y>MTrz`vfif@@~zhvvr`RuEE^_GxjX55tk-6>LXL=f9}v zIzMUOku96VaIPm?_xaAWe?p&$)|hWx$>Y~9I$ctq(7(mr&|X|z*JtpyGX(x6j95WG ze7R_2)dB~7K0^nAap?1D5HYM5FW}b*?WEA|L^z&~J`X9)C}}$(kSR$S+Y0*Oemd5+ zG!;Df*t`5ncLZjoek88ikssqBk6#z*Dvyjvw9;T-TYV`7frl31v-%47^$A_q4_Mx7 zi0~2m+g_PVx~?Pm&-PB~hlmgPA;;gYr^+vY{s;R&G+h78jjhb$ru3Q64{2EV)9xP) zn;`*=`#ni4Sh*jYmdiHK4=b>OGQ#g5P!xdx#-T8|euH5aXBHH-;j^z@vG<)IxUvVH03E8p+@_jX!Z5@Z)3NQX^wvi zzfiwHiN284K3IN^B_Mu+e|;AIm!tl1jKa>v35lXHN&;cw*~Vyqx&+K>l=sFKIy$xa zXS??0%zdV&UxcuY{paaNR;^>~f_C~N@*mZQSZXS6nftHwmWWu}cdF;a1!{JDd*@sP zE-!I{BfT_@Uk7VGQ(F1z43^-m<`z6$V}GL7G;A=>zxLBO18YmERY?ReBj71PWW-R> zku2zkHRQ6tVNpW8o&E`=AY0({{Kra@$lh`}huNYp(tuSXFs>*GzJJIlE)SYdF*{pg zvpoO0$=WJDZJ(~D zCdiUwk5;^<_0sxx?_A!tz4I~6I<>NijZ&`?Onc@Q^uuBCX6N$3Rb$5grt5S__`acz zdrpwLV|}bWU%!F07e(|ku_7kAEjSR-?jWCh1P{Fh{ZL$R8lu5u?-(8D4P8DE1S(OQ zW5Hc&Jh#V$_C>Mze)#Y`>;u5oQpb1#TxK)b9noDg@e3N`3P{sqHps}AHz%n^f0mwe zkpJ2^ZC~=E4J{2g_+2wtznV@OLVSgUeDZ+gjHa7N_p7z?NT$3Pz@YQR`a@{A+}E&^it}S&W?BLfQE~+Dq15=_lIS^7!@T&5r+p zn<42C@)vENjXI7Eywt*Ww6NO`iZk7)M0yjkizpFRhu5Wa|a3 zQ=92gu{L33LBt@uDEj(ds2K7SD7yRp14n~4E%D#u>HVwN1S;{oR84^~D2iLi^<$P| zDlZT!PP#qwLI3Y}jFGlm==)YxIxsP<&j<9IfL5SgYt+A_Uy7==r5n`wJs)J3O$Ilp zTYwn&=(p~J&ukH8sdf++imZVKa?$#TA9}k!Je~jIb5EiCqp`*}u13_?MPv51ZyS-C z*PW^{2#IEHy#_y*q&Lf}yod&Gp^aA6F5>}qi`Z^fjq05R{&f(vt17v&v-lSJ7Y5C9 zqFjpkpLY`XF`k>lnD$GN%*KQfZu7urTC<^+;#O>WN*&V7zv(;;i5dIJK9 zun}LA+Cs0>3lrX#r+-V(IRBI%5rE_0(WykUGb`a@!J$z&y)1wOAH}@RUKg#F&oRQ< zq-gB$W#NDRoepH-A*b-GM_c9eSBA8kz5Z%3eD6T#%?Z?R{D(5&1SZ?;Y5eMf?Szz- zgU9?bQ0y`GK$C&sCTj+)+@kUGmBn#Ib_a_QgcU>-L#P(Q z?;$+QX<@HZqXXTB*c5&Ntu%>N)fov}0%3f~-DBaB#YMTLMh#05%SE49Opo&ytu@M6O=atkZS!B%7_2N8! z3DHlXV{jMa%3)q6K9^(*O8QMOyF`J1y-VqIBw3oAb1pi08n|4f7U{2u#Xuq>&%aV| z@2%XIRj->j1Rg-dDi9+%c*x@y?29ABI-rhmJUHGy)glOq$GlC2`ok)II1zdnaQq=H zQ$hjH5w@tKzq9(|XsY~U;&Pg^MVudq24jZC(#ghf$SSFcduWG1Q4N((@h?j$HxG(B z8_eZR!b8^hlK&>_aj_s0kwD3|()%hF1<dL1 ztUt6)(4n|*FZu&2ItZa{r4|xzp2nGGH1{ohPj!f}o`nisW zgEjbDJR~bRUd^qk%r8%6j%04VuES#%qZUHLn`ajv%h}g(X^e(7Os~YRVHBU>;ha6r zBDLN}d-M9?WyyTGPfC<{7%v0q(`Dgk=?wfDk#>qU7!FDkO>c6=nE4iRN#2Z_*AK@@ zmL1*V$HQn$0q2DmGp%dh`4X`L9GMZVaNb@lc}PuiNH_8uEIHvMfBd zYhQsP&O|h5!Noc78vz@+{$nCVUNV|ko`1DiP`@1+hG84m^?}r!t&;VJo`(U5?t2Dx znYCXY1MPZ~ekSf5DBGZ(QvOqZGn0SiOeUqcdv*6u#T+mfX+$&XH>3>JYL`m>^^;_) zU3Xla&qu}ow=al1(G-5oPSQb!wczY*>~?S~^v6WT85`*@X5_!{IV+Kkb|b7v*VOyyLSq|T$j=&Awc3>Cx8HVJIh1)w1AUco5(h!yr&~`&b^og41ord9F?IMLJ}Gh}i}HMRHu+ zpf*`z6ck37IZ!-8yo9h<;Z^ZL{_9f>J(RTTu*9Zm{uLB?{#EMsnb2oED9Z(Cm;7eA z{3c(&@d^FASSev`_mCjX z-crN1La49lR>B@H*;tf>UZ~$_fQi?wGuE)3v`>6|hKf>iyzD*L4(c4rne2|NKQwP>%X-RU>Ua4Kx-EOpf2t4?%~q<974YjKa4)yKjip+rq)@|wWOz=(hpI8s2>FFa&yNr5r+L!D)wo25-e_Kj^{e`q;gPHS)MdI zr9k}j8ym``3JMe+XW^WEMJrA!2Qt>>X!xdW)N5MjUJEmfT?8x$i90oS&!hfOd`!rH z)v6c732IFFY&F16uBjjPb@k@;L+m;oxe}?DU0VgGCqZG7nwf7Veu?AePzT)U!ocxl zdCY6hso^pa=G|ei+;5?No;Gu3YLfeo+(lg+v+mP}MlrzolF+&|L7RV;3pN}!*PpQmy^A3&V zeMGfHnj<4-7xY8dy*u^cc3Ho%lyBeBK1M&cTf2OndHqniO#cS`ur#Ae{cy3w3(3Er zD0U*L>n`gL--@MpDI`HUN>WIai7+1G-Y2H@!+^3+ z!dA!jqf<^Hm`Ar_b=7|?L^uu$k z5@h>!;k5xbr>MF|f4_HzehAqx9jJ+WWw~;uBL^h6l%gKY+|K zf2#yV*URM=ckN5SXCmgeQMU0R8vAV{?2#%K@Jretm*bN0Az+K(B^<?6>*ftR|lI_wJG|2wQ`i)kx$~GvcZ`ij^kXyge2_(8KVQXT}Btd5rsFTU#*E>je z0s{L4xJtwetk`XSDL(P1l;kTw6gc}^5Lp!Uen z*-Z0;0uutN5gM|BIy!%D8o&0-k#7`lF|1LPs?+Nd) zk;ktjZKAyh3q6(ECJTvXj$8>c;2eQszJC4%`Vn$==wXC5rM`9!4}WbT|1}Zaur`lh zqh!ky5KEZOdo3%g7c)-791~Sl$bbESKI9D|INsaLMyu`+TlcBopsPHR3SLz6_?7kV zma+Oq^?2AK>0;1V1U8@WD zRdgD#1&KiVjpMc~utb}))TjW+3iyRx7b2uz&(Rs~2W+V+O6>R7&cv@*2^A(B*5ufa ztf=B8o&M95B#ZwUewl7I5PXpRz>6VgkpJodqTxhO9=}Eq>`(DQME$ZqzZ9rQj0S&D zz^`W#SFA{zFIk=KyR<_#?v=_Lh}iIo0)7cZO(r6ctbR6jfV?tDmi3w5zTRp5u;@<6 zhV{cn);|&3!u_^qkbNj#uBokpLV7Sp}#sC6Ja(1$Y``TC7p z=n-DWSIV;aO;nxnbjM}vhmz|2KAfRt73T;WrsJcj96>~wocIvyV0Imzu}I8 z1FhtNDTpC%%wO`oKHm#7@#{ZDV|M$Q>g(jDz0zugZX_B9eV?g${`F7bR(t;v$Iwv5 zAFWFo;9v9$y-FD0n&w1AyDS+)B4TtozGF-vSVEr@E4$&F@tA0$;YeJR5)J{3br-_Z1L33B5oxl9N%Vv&sR z5%nT)IkI*8K{bzGpYW5c)xk1dvk($Lw7NxY7B|dBfs9yQKfId~JlO3K><_VF2W6rH z*mU^l5}~)i?M>kqP8ex8LNy3#2L9D^b069q(EUi3`SbWyL#WnpP(?Xv0?7EyDUXiA zi5d||74T~v>?~E9*B<9Y*FU?O; ztJsUM5D6oRxpCk@2{HdPem&#wR~9A`DONAjUY5kKh9`C4g(&c^_s9|9B-BEcfL{S$ zs(KOlS2$T325)lXAJiYxwUK4ndZ`xPa4;_UaE%aeOc;6mvian?hF!jAud_>$co_J*+_vy*vkcuZjFux`1C}l$ohtVz9+c{zl{48o_ah@-4A-9jBmSdM*s?n`v??19u)TODR6?ZzbNNr1hr3<>Y} z4E%!B8X-s@z%RE@3a%haN6hU<;bh$R=;o_*T(}ie$hnwHyLD6Y6gwQ-nk?kM)?sS_ zlJ>Ck{3WtF0ZHH&!v9_UR%|p=_6Ph+^qXiSImlsJDC-Yvm@LK%WN^5K%Qm`(EQ1Ct zFy)#80&2_Sn&O@a>zuZ);nKBsR6(C@bsU8pOKb0m8-5hh57l}wz^3G! z)6nZ$U2IAr*1|V={#A61{R(zkiZfz3_SLw;o58qTB9>8#bMax0e<6aE{A(Dd?*@31 zks%sWvi$?=3YBkIm6QdaG{h0lcjfF0{3{Ipb*U6uF7_(?yu{o~c7(4^s#_=~=P+RI z?Zlc^f=Wa1r=dz8spJOIWt$58>rV5C7209E&#uxIty%z}2k}rfP?`m|;_iD;A9bKA z$W@a@{f|*V0-0CtG2scKjcZ@F;?^HJMn~Ik>7NzUZ*&*$(QpFYDcMjQn8q*EZ}csX zZI7z&(D%iiS>J|PW%O_2`D`dWqkaP(GV(V1JWB7;Qb*b#|F$GzGxWockw1)Hi1846nVv>`Sof_t-^j(`geusd zRW9l&o*oRclF`6Jbqacwzz?# zm*!!rb>PE;>{})Vr|pYqNQQ+bDpL=j@&n^qmD{T`&VQ{(zQ8>ZpT(jy%W(zu z3k-}SmDdl!zpk-6oRTa;qvevG0LY3_G3C@xs9nYGS~#fR7!`+Q{_9@4C|v$^ihgS~ z;vBsi=D9X9|J4U`7%q2@eL3J(<8-*9R=_}}@GD)ECb&3>R-A0fzbuwkuptFT;TIN` z9p}HmzpMeLD>W}j{T%#jn2n3W_kTkKhAEu09h!ZV9BSIRt)}H&XPeXyx_r36h?0;PTbVKlQcltDor4#GaT>gdpSGYlV z^SSjj^m&=vkD}g~$wZjm4ZY<>Zo(u8JJ$+~V5qQ)v2F29QoIJT`HK_1P0&-h$nOG{s%vWW3)CQ8Weufu?B_jy zrXP}DRwL9_nY8V5u zBIq){Vtf+6IMCV^dRraH+1Ku(5eAtK<5EZTcj*x1P_@Pc5$gnA%*3xEpq11mwvyi; zNO=4MTeAK@-~`Suhq3GwExInd%5~(wl&2As;Gv@D>88E%p*$BKR?d9}yf+sIm> zcOI*FuF+->dOeM-vWM_vW>0CWB66dN?AX@i>fm79%g&2 z12iimvJ}m=k@bf-_GSAvl+V}R6xDWU+&Dy^NFugI^)UB6Q|FgCpf)R0tD@9y?x}vdy}qL{7W}$mPUF`u>NBhQ0{50zh!?8dq~vA# z_b^uHfkOS^bxpg0$`z-xh45c+Jllat%qA9l<4pniiss)*2bnS(}9 z;Q$LRA_$B)WzS)xF}ID0P@k3wZ^l@v{~o@@E6=&4okkJNfPYzT8|u;yYZkxDQlkhI z>jlb0Hz3Mw6oJke_@&@x7_P_5t>m)+pvojJtdO_jcx>$6PWc|FpOhlx8NzIpBFpBZ z@-l%Q^7AI!1NZ0t7W9otdJfwLQu#Q7TkM~hhCqn0At|?B&<{1p8ji19hVg_n3iuZs z$erZN7*n;fPSFI729}k2C#$7y+*nig$KHqdjiyGYG+W(0H9zKilyI7h^kFrm9}=~? zez}eMHpTo$^#{vSxp__$9i45npyf#+d^${l6;C$!IY^ClLa&K8DDFHvbDvV46`>f~ ztf#|PZ71S+uZh}~qq%u{%AoLJzAfIKpK;>ZTJ=vnRgqR)toM&kGT}6(Qt(3HK?Qf6|mWLBnnlHVZTzm(q=~8}} zLP-3n5=m@5*h|`LCUd|m^ZMZ)U1qlp_&Nynb&mcqHbgIO4A9ql8ghQjAz5}<7hAcZ z{7rh%sk6VZfqhT=zP&yh{W{C$<_G+%>HrI60NE!1TcI=1y8n*;H$CWp7u=g$_wl)h zt?RsX(D$7Bwz%#@l86(&z3^>10w2X@;MZ_>L|-jnO_Npx7T0cD<8XRDq3)!x9G7l> zK$m(OR(N;M8A}|uBF<{~dG;1HcLaCl@rxc5FU9;FfgRPTKa3;4vzvXMHd_r*T!Lih z{Fuyty(AU0>}&Yf*q9zPFD1ocE_R_ z^&4yL$^8w@UV+(2Aj$4~^rv_vpY^1AU3`0BSy$PU z>NCaep06yDA(or}T7nV?z{C!th8?G+u~KCEa{C$gZ$R4cBi3uPacz|4AJmyYenu<_ z>nDsU`~v?%pJN<-*61a=pL=lUmGv`y!M!D!_^F(Ip?=GQlK!!}Oi*Lk|~rH1>!tv*jzI&q#lseX;i8>P^nDW3rTWg~hA&z1QUY_?KUl{a!k z_@hbnD>?ge@hdij73>B7LMcv}i(gtZs!V&gXqH^lKv7f8MyGlJS0{by#0dw%1^{Fp z0GY>9^ZaWS*jCLcV=+UqP&}AsEl-x2YWlhCH{>xw*B{!-e0mJmZW!JEcL-;xIO4I} zw2j`XW1e+*A(y`;@#{@-d+fFi>N{*i)Y)a7Y7={1+=dtBWN=hZL!}wPDX*yW6 zNa@FcDLPHh>UZ7ic`i54&<|tK58L{?0b4J~n#oR&8G8aY7>%)xoPAjpBXpp_viig2 z$YkRh4%AB&(QYlU!b9xIoP7<>{V9Fw40Zbk)%V3!*5JUt>JiUNy~XIt{g$8$Ni5SE z-^=Rz7Bofz>PhDWzGpo8Hvi zYaC?#Ml1c)DdfL!-&eBH3W6rL{Z<^vE=a4_`ra`cS?L)yuOE(*hb>L$@mVoSRQY8g z$HnZUv4OAXJDHuEA9;Schn{N>U=Pxd^jbbI%tmQTtVWcW_H_Nm?EQcMzoVxx8<0oD zlY%bN7KU$<`TF_U7c0{kvKKh$JfJtF%lfMS5Wforx~01d`r*qc)rKzW7#NPzN~PIX zt_ys#0+oAt{Mt{)t%V$B^-cbfwY0M|rT$JkA?)4QwE6lC@UJ%iS^ai(t$D`p-vzh) zd+SXHh4i}-NV;~BEn2T6X&u!r_HbeIV6sF5Ngtp<7>Avw^urkJ>xan)Y}~f-J^l;v zz&ezmze7W(7Fqq@_3OF!NdEN(9ptsl;FQ%`sC21vkUDPYj`-7+w%?`p&yGyF+Iu}*e+3Rm2B8}kBHk<*+nzz=P&BLDz1%^ zB`dl64`BmwLha?9G~@j8htw>*lHPuW8b#^2`VZfDl!ipfjPuJfMGGws;gj5csO776 zzcI`D)VzK;#*fpXm^bN{a#llk+3sr8i;5xCy7k!}t*s~F zUt9V(P{x#VDDi7EE&3}^k6q@|$N_nh#q0FVmc0T(Vxj)9ZJW$?eRNS+&5^zbM|a;WskXA16Mu zk8~C44|f;IDoVf>WI9MXxAS1eXb{(MploZVkpFUYsTKwV>;n7?L`?P5xa}WACgNny zkLkccFsT^W7SefCgK@F{Zlx?s`h-ILhK{-vgexSuCqiNnRckLn{l?gH>u#g|@B$6- zt$UOj{SsRmD>Hj*5D^WbkfXYQU-$-WYqvV)e+#+D(mg8f`@&^z0K1v_FQt)!U{2T+ z4XkXFYMJRA5zF(h0Co##*C5jgaorAmjHNKZ7nhdK&<|I;TML^Ytb2!4IW{d0DA{)z4w!HKXBbMs7e5ExhQ=$Adi^o#)bcCa&u=c}AW`TQ67 z7Ye?-gxvZ9Yj3nB(XW^HTTdy^I3;Ew|Fs_>#C*qlNZC&h(9+B-GY-uP2ZG=ar+!O% z(T_yLT8$D}nv6GRw+l5P1CKY#uy8s5EX-^4D{SM?7`vUl&X2xpGcTjaGi?qz^U*2VjE6zmOS_j36j z)XytuLvW{#wgp)2Ps~do?zNB3%jdrY_!m@bs+CdXyER>EO zY4)H79gm9#E_*hV-9-CD-G|73 z&DZ2v;FVFBG?()n)dAd;I_7NvJDxCGoZEJ&xur$#dKFPq9>2!0?xOg`(8}qAoC0D}T5@W~ z2p8%%kpDWqJXvN}U!kjdYdn~(9@nn|zkC<_^Yt6=Zjs(k5$#!PLPXeV7r%H5&8*)T z;cwXu-|#+J4PHqNow%^#)xM+fw5#A-G}pd_wvIL*^LI!0w2g6`J%I@Czi4f?{A&>Z zsaX$Ca-gEY(aH--sGi^20&cv_N^x3}FuGLSfm+49y>Nn0*zmLrn;q^1>=OzDg z%MO=d;oL%w8hq0#$Mxzo|AI76C?h9Xc8D<5aETbAQ+R+peo6jS>y{nrNQA`<5Ham} zTF+9r0rVxXn|5MP;M`zmF96ynT0eu00z(bOFT`(wv>q}#Wvw?95KZ4CRXoJynInT0w)AQ*}Q^17Cfeh{dxX%i}oU~ z&Y%{(l71GiqLmM`3hPBZn86Mo&%H)<^dfM4l=A z(5g6=JKjsIn8U2JmFBTkv;z)bR!rsg{NUUHx*~AeLSC=*DGi0UOQ{@!l7qU7so#Qr zXie5cI*@9)AnPiVzTI==Ek>@8IE7z|`A2ux;rfm{QT*1}4Odx(8EdtC6NUQ2E*kC$ zhIP?aD-QG8E~9eZ=V>?^nhhh$=f7$QK0JoILxXgf`w5|bz&d1xMplLk_*F(=TRdR4 zX;?~^S9D@TmvP|LkI(OkeU`b=}nQE=^sTrc;Gyduvp7w}7>RfNOhYRm_as`Cca z06#v66OJ?PpWlPn5BlN6mL`}C3OPFOt?cWisyPvlNRbXX5_!l^9yFA1E(ZVHZRK#rLcheWR7B7 zdHnkC2>C)Q^gV6GT94v?II+t=!rp?VfM4YCYl-z^J=mr{4(fr6d^36`4*bfs$>Udr z{)idu2q>N3_5ep4WtJn&-rGY|AJSS z)$%9W&-Yyc5nG*9Ps`l;pPgSu8x3*P?q2Z9;vuesh`A@-uvhc>uW^)ZfPeJ?zY-uC zE)m218_G(d7W6|%!PPMxX)pZ|22Onj&QsO{7-n0Zf9;36sJ=hZ*>;QKCd)F#lB}T` zb7w(6d>wtBbvelZ+=EQep@-i2}( z#pK>_>*pog8c`S1D98B%M-5kwa`&KIo_{Th9wJNVd$$CvEwXHWu&tJD5h2l!+*a;f zWDdWq2_2~f9Kf-%*kc@Lw_RVC=U>R)@~Vu^5n0Kkl75-3);4kM@P8=qubW6-9|Ybs zpGh4F*KXX)oB?t_~nS_M{xG71x!;!Yld``@qq;eg>gp#ztBSgIsN4?)brP3OHX-)+(`JwxIA5} z5B*U4GzQPl+GTxjvx4E^fE&(w;9*K1Q_RZ*59!>U0`0;@QUcp=iF7hzbxL(Qa{NoX z-#o;FM-z}S&CU0PzKnXCMYt~|6a#)`xhdyR-oGIkM+wR{q*)zXY2o3p9(q-_AuikK znqF}LZKA|17AIU~nVd&c_%$6LlFWZ85v5cZAW?EH!a1HI{u%vIJLP-qTQ8%2!}W(q zqtNoB(*h3nO%&=6KcQx5TUeN5rks2KM9q0M8rB!sIUo~JFM z{t}ddtmcj#_s`p@m<3&p>6T4M-l2Y;VIMlWRj8ler{ljOu^0*jQ|cnh$^!UD)c1LsTAZK_-sJWts~oYeNQNlO4kr`Kd3{1Xk5~dL zXczRueO%rL>%M1_LpSbI7l5cn9Isu_4@0zBSbd(b`Vk)nrhrarSd@Cl`}?+`+*)Ld zRx61p%qrYaj~viGgb-y3=2n8$PV0x3GM^qvR=r<~vP1htQDqt5zUfKY)Kiz`T`ZA% z5At7aN294Pt_mAoI$$jkzQw?=W4y(wn{oc@Aeis+4)En`xXuxK?2`H&j)!A2`4?`? z7DEo|H$rglDefIs0UQ)L=G&{z&<}Aw#nHRfRS;+3>#!&|`(KyFMN)bE`XS=p>TDuc zzahbw&7+M8AX*p8oMMZPe55fjhk<9V~Jal4){ON3h%; z6B_V~hj?wJXF&{!FknG^L>du%IAT9j?)jX47($H)bjY8REIagHCaC(nUmh0YvW*U; z!f7wZ0}8`2Q5>LfTKWx%gH!RLy#MgTg$Q9>)pEkEU;+sd?4Md(gc!=%*X~vyT|vm5C1>dehE?(das%bRl-xZp9_o*N*l)ARhx!CC1LO~F>B*LOTa}FLX7r*lS3$o9Oh#3^UI2K0@+Ey&C zi5sO5oXj}COpogL?;w1r{v$m~%m$r=mTE6esy5q_TMPX6Qu8{xorOb#C>LOZx%Uj}^V)tXmS;;hV3l7H z&u4?Ue;yTyi)BT99=~k3_ZATft79q#DBXn{4a$cZ{OhDxZhQZ+I)b|qmc`08R5#OU zv3MWq@bmmDNl3%>K`q3w6m#)?x~z{nXv+GiLjA^W>Z`2p)Ytk=8BB6dj8*dgzrE`X zj;p%v=e>O^JxMF;t-MPXFz_r3d*d!<<$oC4v9DILSir@b*nrdVbe^ea;z=nZLx&KC zBvh6x-QO)re!(;95As1Pr`E5R3)b210f77)RS?I0YON{ZjIgFx$n=e zESK`3-=gQV<0k*el4LORJFg3i!&y+Hp3qUI!;J#IQI~(6iVZie&PHL ziFH`Fnfu|tN`%diMHY*fV{^J=!wuA}WcrM6&_Ri5e5$dHzF$EYHZ{)0gTZ%!ol^mS zT@9JGa>KzQj=;6yB>aKutMjRUKraXS#cZWYK|F2{Tm+nwCiKjr#wv*Y%iU;;g>Lgu zM46KW1Jgzdb`%awia(^6Wy{1I?302a*5WNdVCV0#YW(48Iwe=I+4YHab_dRN+!$Gm zpoor2tT*rQ(SYrbaMycS6qKnC@8 zdYtBRTZHi&h@K_;Sb6{l0$-0qNFbBuMRsAzaQ!QJQoK*g3z=Q7oOI!ics3*9aQmek z5)1RqmR@^`|uWx(zh0EwEBU zGqmjq!)BSWXxhO%p^Uq~rybHTEU(hRXtZ>q`9O93>(5d1nV}o4NB{ZQGxE-2(*i_} z?{m_}8?#snE^s~-?J_zraP1bvAHGS?KlB&nrZ?&uY0tnsdl#0Yy*B0Zdp{N{N1ClW z#_2h+atv-m!-C&PB2uLl&5KK1F^KLLoG*&-A)I^gpL7q8;r&(n>xB6E&SkPWJ+lpg zNlPW#op64_ka$>jebbw7jN$!XI2N0SxLGRxuyFzh-RyzxK))7x^8`tc8_n{MDmTIh z2{Y(w$6Q;tkkmrhxbC3O;mRk*XuGg2Ggj1WDd(PU9MYSOAJQGi^DB#C{QQvpYR9sH z=BJBamOnSID`zU;(>GxlHXo1N@7)XUPiZ|ZR=2jeH-C&d(((j0f=$R%0*>D!k&wSe z=wAj_IkWa+D)Mb@)t1=bLSgZOkF8*|kiWJ#yWYL4GyQn$Z^Vn@F2@{*tdXzDw)@O0 zBTmR)IvA}7uMYXN{bzI+j@>A>#~(Jak@;AazYNiba9l-OFAxl|ct*r3E}s+S9MCT3MVKK(FJrIAgrjj_wEk(w2j%zyBsV zd@tBv)|iRu?vcn4uYXywB8Cg>yGg+i3+=Ckv?q^gDsNjZ(w~{8ON~4NNb`8tO`cwW zzZ~>2jBWgWhrTi&tarn{}uEah3IvTyhFC{DP-ngYhSg2h-P#oA+RU$_}ez941vZ z2Sxx;IYf576JSX|@dSsKGJ%NV3oL;iS6uyyLJTb$65M2V6@n#M%%lWH10GI zn?V>4D+w4`N1JibI`>Q-sQvjN>@TrvHoR)Gu3{Y$a7|}hGbt~sp)TaF81)hCuWW7{ z?;63TkF1X3YGrO^pTA`DomxMp5a4ATfuh5#$Tp?D^;3$JQPa4}SDe+^e^}47^*aO0 zLlZb3`vq;ayRIgF9{ygr+Cl659^s~o92^LvHtro?H-cX&yesTQ#AdKA@JnA{Rs)BNz zWn0(pghc3X;K{|&EV;O#u|BzR^V8XmD_Q-wjAt>TU^t`<0T^vLo`VV6b z|ABkKZaPIHcpgljaF7%|;9}G3zjO04e;r0dkSfJlW$d>Mqg-HWY-FzTmnubN2DdxR zE;q*Dx5%*N>FweAm*+nOTd8BQ;g-+wCdwr)?53LaFD!fE%wdF@?8L)2J^=8t{Bi}0 zch&VTF8dL5zgOW0$W-R@`G?O45dD%We+|-qMOKv`DMwC9m|cT2_D4?8@Aoa2El*e9 z{{?R$czK4hzW^HKb{JyjT-aY#{^F_rLIExSZ2_QM?Rcl#!cKsZK)<%o^*Iik*WrH_ z_p+e@Z#+i%X3XeA{-O@iR=7N&8+KoJI%isV+2tXVV65p!y*5SsAxdEvuQX$j{F&3Tw~3EgDHz&?enzt9#UgW~D|^MSg(#0$HPy|wFK=j3zcYfj(=EPEvUhu1gp`%}JB!2;Gv zoo@yIA+IUGgejwoa?D%(vop7%O;?Sqo3#G*nOx!E^agcoFQ4b|xOmlFZMU3>xB-7* z{fn;2H}Mf(KQXQ*xaHrVod$flz18@QC-8&_O%(buepCmJ#JzF&7zvx#T#vu_{a*(u z(~edi5eKP#OH-ma#=ac^A{+DCozLIE_9z!?kxKDp%Ym7LmMd2wr8a&8V+Ascq2feoJecBCS+k~LncWcJph&Tv5TuRMS0esb8&neH-6`@Sd$o9qTF4Mxu zuYfsHmo2H3pbjOWV}nEq-sb?|H8Wm{P3n@mmX&MmnWizKzSo>y!m2k%~?N8#slkhqN?r!A_c*@Shp?&uNx4Y^YU^EX{x(v&JDs3q;oBbXIU1^Gg6(_CTNH*V4$KTmow4 zX;TR5gA~79qasqHUwi3ZsFl{Mc^VhojEt-nqb{;sAEaDN5BjYx!*W9zRx77gzm8Em zN}^JHp=i9Ldu?H6!Wh*$#M;7ofM^HhkpivsuQ}1J!++9y+wSC346>K(jz48=buY^z zMFuj2UW2-HW zwt1}rS7%Lt=wqBB0wGhoo0tot0T(34>hJF4l(-wn%t_^sS{sJu?2W0*h)v zD6`_bx4iSi7mj~$hQXuP9e?=Diu>|MXFhZM)VD(!P45*?So4gA;aV_m%}X@6fshg1 z7V>fxtW~@m$mkc28%EJfNf|PbA-V{k1`O*9na17PEO~<+AG-jgt8MYc^0|PQ1A=z2 z?CoFkjhp&f%iV+VsUUb7H%99z;YPFC+9^O-o|LQTm;zp|0YZaz@@2Y0DM_vVgo;#^ zVV0k713JD4P#Fn4U{+$E zi3v-%3c#mGag~U=*Cammh!ul5fIl7z?P9#_&rqb7PeIEmIu3bxQa-`Ggi+8Ew1(of zfD$l261rEeJk=4lSUW~an+m*aC=ZScc$onL9gHx=ml5F83wT)(^by6&{y4Ao7~A@0 zIX9#<(5M1&25XTL^i`qt=-cEIpyP(4wP2i6T<>OuH9Y~F2fWNgk9@%IBSCxpulmKj ztS*7FZ0%}OX^)n)-8?hrtz-b?8d4*C*hWU+Ww7A|$}xC;C=3Ij z6I2Qyh^@rnvP!YprrD)ivSb6~uoev$zKjXIDub7~hc;}ShsM;HQK!)k*e*dfM8#~- z$xLY3BJNv;O|zKAyvv&zia^SM;e#((Z=t}UOscQWCSC^GD;sUIzYR;nvduPkBgN0j zR5Jo8(W(0hPbn911~af#6e*!hV@cmfgJQnLV4T5aJM?|BEEe!8GG8X25P))_6loMk zfsJ)@VR^$^_e3CLmrN{Zpf&((1NCDK?8vgQtcUlqtooH2JBVkw^;wND7|p6g+cBNJ z8>gsyJ(}}Ld--Hhy@WLriyq8wCa!1yc_571hO(;gx>VBTY|Kx-r0su}ETgXK^Ln3cLi6!uljVUp%?=(?eE~f^f8J$yRRC6h z?;S726Tsh4X7xLP@Hrn`L?+DV-0F|q@OuW;d_tK3gmSeq-*f$6dElZl-lO7I|E+{W z8Bdox;R}W?QyNa;VkxsvU;}ea`IQQT=LD2m5Ua3Zsm@lFu}Wr#iJl2vHacXC`zn-L zTtt_Z*MT(2U41X>vg{1|JRVExo(`IFt{r1#G`E|!Y^e4zPnXG4VS+OW-ApGNx`e*! zw=DEj)@7;RZlkoEn~kvwn{t6oyY(7fHZdz9bY*0KultZk3P3$Um``J=tt_F!vmPon zLaFbWpO_D&9%H2rWzxna+PGJo`Cf7QjfI|4hcZeBW2m1($?6~;>qKdBhf$-0CUn`t zRQ?{CC85jXv`tG*AY~jW12wu#7%HK;4$fcUe5`WGz?d*umwEg{ZH!Pm0LD7Botx)! z%AnzS(v0(FbQ+RI7E!0V9FXlQDnul#}%NNbDYPmppEU6$VOLZ$XT5_H*- z_>Og11RzMM(PiOlZUlV{nvBa;DIVs}XhQW5e`HXS5Cq@gF1ErcN_pMWA~bRBRjWIp zUU(oByNJx^>GC_C3a|d?{Qs%TUpfB;#)Qw85&W3^gbMJ7FL+^DPU9|_rQD5Q@F&w^ z(={+%1JgAyT?5lKFkJ)FHSh;j18Tm62@~c^;t#6e(}kU`f$18Uu7T+qn68288knvD m)Ib)|d_>m{XG!8eMY1FszDULR^3g1n`FDOk!{52^xBml(CtkV$ literal 42178 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 Tp;7)FM;+krx$?49kjei6eNHWv diff --git a/fpga-xc2s30/fpga_lf.bit b/fpga-xc2s30/fpga_lf.bit index d8ff55b3210a7e7472795e7411e170dcea28889b..93a26841a35f0e01f50465fe6acfd602abd8e758 100644 GIT binary patch literal 42168 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Ξ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 Y@BP;AUzNsNXt>3D&cRAr%74iJ2d|!5u>b%7 literal 42175 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 Y@<)FAnk41;_uxzMdqyj1DZl0a3*Ltj4*&oF diff --git a/fpga-xc2s30/hi_iso14443a.v b/fpga-xc2s30/hi_iso14443a.v index 81643b8c3..c9bc64f94 100644 --- a/fpga-xc2s30/hi_iso14443a.v +++ b/fpga-xc2s30/hi_iso14443a.v @@ -222,17 +222,29 @@ reg signed [10:0] rx_mod_falling_edge_max; reg signed [10:0] rx_mod_rising_edge_max; reg curbit; -`define EDGE_DETECT_THRESHOLD 5 +`define EDGE_DETECT_THRESHOLD 3 +`define EDGE_DETECT_THRESHOLDHIGH 20 always @(negedge adc_clk) begin if(negedge_cnt[3:0] == mod_detect_reset_time) begin - // detect modulation signal: if modulating, there must have been a falling AND a rising edge - if ((rx_mod_falling_edge_max > `EDGE_DETECT_THRESHOLD) && (rx_mod_rising_edge_max < -`EDGE_DETECT_THRESHOLD)) - curbit <= 1'b1; // modulation - else - curbit <= 1'b0; // no modulation + if (mod_type == `FPGA_HF_ISO14443A_SNIFFER) + begin + // detect modulation signal: if modulating, there must have been a falling AND a rising edge + if ((rx_mod_falling_edge_max > `EDGE_DETECT_THRESHOLDHIGH) && (rx_mod_rising_edge_max < -`EDGE_DETECT_THRESHOLDHIGH)) + curbit <= 1'b1; // modulation + else + curbit <= 1'b0; // no modulation + end + else + begin + // detect modulation signal: if modulating, there must have been a falling AND a rising edge + if ((rx_mod_falling_edge_max > `EDGE_DETECT_THRESHOLD) && (rx_mod_rising_edge_max < -`EDGE_DETECT_THRESHOLD)) + curbit <= 1'b1; // modulation + else + curbit <= 1'b0; // no modulation + end // reset modulation detector rx_mod_rising_edge_max <= 0; rx_mod_falling_edge_max <= 0; From 74bb64f31666c47934d296bb7d6e491582bc1501 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Wed, 24 May 2023 07:01:33 +0200 Subject: [PATCH 54/62] formatting --- client/src/nfc/ndef.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/nfc/ndef.c b/client/src/nfc/ndef.c index b0a32daec..311615c2a 100644 --- a/client/src/nfc/ndef.c +++ b/client/src/nfc/ndef.c @@ -923,7 +923,7 @@ static int ndefDecodeExternal_record(NDEFHeader_t *ndef) { ); PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, "Payload [%u]...", ndef->PayloadLen); + PrintAndLogEx(INFO, "Payload [%zu]...", ndef->PayloadLen); print_hex_noascii_break(ndef->Payload, ndef->PayloadLen, 32); // do a character check? From d25f20fb109e676220f607041a3c1d10519f6a67 Mon Sep 17 00:00:00 2001 From: Jonathan Liu Date: Wed, 24 May 2023 11:03:02 +1000 Subject: [PATCH 55/62] hf mf sim: reduce 6ms threshold to 4ms for reset to idle Fixes some readers not being able to detect the simulated card on second Inventory command due to the RF field being powered off for a short time before being turned on again to reset the card to idle state. Closes #1974 --- CHANGELOG.md | 1 + armsrc/iso14443a.c | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5783b0f46..85593f3b2 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 mf sim` - reduce 6ms threshold to 4ms for reset to idle #1974 (@net147) - Rebuilt the Spartan-2 `fpga_*.bit` files to include the `hi_iso14443a.v` update (@d18c7db) - Added minor orphaned change from `hi_iso14443a.v` in `fpga-xc3s100e` to `hi_iso14443a.v` in `fpga-xc2s30` (@d18c7db) - Added python3 script to convert amiibo nfc Flipper Zero files to eml files to be used with Proxmark3 (@OscarAkaElvis) diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index a8cc1414b..f689d6bec 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -2084,8 +2084,8 @@ int EmGetCmd(uint8_t *received, uint16_t *len, uint8_t *par) { if (timer == 0) { timer = GetTickCount(); } else { - // 6ms no field --> card to idle state - if (GetTickCountDelta(timer) > 6) { + // 4ms no field --> card to idle state + if (GetTickCountDelta(timer) > 4) { return 2; } } From e3f4012d78a745dc2344f7fb44f1c79be3089100 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Wed, 24 May 2023 08:07:35 +0200 Subject: [PATCH 56/62] maybe proxspace gets happier --- client/src/cmdhfmf.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/client/src/cmdhfmf.c b/client/src/cmdhfmf.c index 8f39ca3e8..8d862b78d 100644 --- a/client/src/cmdhfmf.c +++ b/client/src/cmdhfmf.c @@ -137,15 +137,14 @@ static char *GenerateFilename(const char *prefix, const char *suffix) { static int initSectorTable(sector_t **src, size_t items) { (*src) = calloc(items, sizeof(sector_t)); - if (*src == NULL) return PM3_EMALLOC; // empty e_sector - for (int i = 0; i < items; ++i) { - for (int j = 0; j < 2; ++j) { + for (int i = 0; i < items; i++) { + for (int j = 0; j < 2; j++) { (*src)[i].Key[j] = 0xffffffffffff; - (*src)[i].foundKey[j] = false; + (*src)[i].foundKey[j] = 0; } } return PM3_SUCCESS; From 2103d695f85bcc9ae1d4ff6240bad2975861b9d7 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Wed, 24 May 2023 09:13:01 +0200 Subject: [PATCH 57/62] maur keys --- client/dictionaries/mfc_default_keys.dic | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/client/dictionaries/mfc_default_keys.dic b/client/dictionaries/mfc_default_keys.dic index b51989bda..7ae6ead7f 100644 --- a/client/dictionaries/mfc_default_keys.dic +++ b/client/dictionaries/mfc_default_keys.dic @@ -2150,3 +2150,7 @@ D201DBB6AB6E # Data from Discord, French pool 9B7C25052FC3 494446555455 +# +# Data from Discord, seems to be related to ASSA +427553754D47 + From 5e3710c232b34a1d666b5ef812976630059da24b Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Wed, 24 May 2023 14:02:54 +0200 Subject: [PATCH 58/62] rearranged for easier testing --- client/src/mifare/mifaredefault.h | 51 ++++++++++++++++--------------- 1 file changed, 27 insertions(+), 24 deletions(-) diff --git a/client/src/mifare/mifaredefault.h b/client/src/mifare/mifaredefault.h index fe11a2a27..941abf41c 100644 --- a/client/src/mifare/mifaredefault.h +++ b/client/src/mifare/mifaredefault.h @@ -43,10 +43,36 @@ static const uint64_t g_mifare_default_keys[] = { 0xffffffffffff, // Default key (first key used by program if no user defined key) - 0x000000000000, // Blank key 0xa0a1a2a3a4a5, // NFCForum MAD key 0xd3f7d3f7d3f7, // NDEF public key 0x4b791bea7bcc, // MFC EV1 Signature B + 0xfc00018778f7, // Public Transport + 0x6471a5ef2d1a, // SimonsVoss + 0x4E3552426B32, // ID06 + 0x6A1987C40A21, // Salto + 0xef1232ab18a0, // Schlage + 0x3B7E4FD575AD, // + 0xb7bf0c13066e, // Gallagher + 0x135b88a94b8b, // Saflok + 0x2A2C13CC242A, // Dorma Kaba + 0x5a7a52d5e20d, // Bosch + 0x314B49474956, // VIGIK1 A + 0x564c505f4d41, // VIGIK1 B + 0x021209197591, // BTCINO + 0x484558414354, // Intratone + 0xEC0A9B1A9E06, // Vingcard + 0x66b31e64ca4b, // Vingcard + 0x97F5DA640B18, // Bangkok metro key + 0xA8844B0BCA06, // Metro Valencia key + 0xE4410EF8ED2D, // Armenian metro + 0x857464D3AAD1, // HTC Eindhoven key + 0x08B386463229, // troika + 0xe00000000000, // icopy + 0x199404281970, // NSP A + 0x199404281998, // NSP B + 0x6A1987C40A21, // SALTO + 0x7F33625BC129, // SALTO + 0x000000000000, // Blank key 0xb0b1b2b3b4b5, 0xaabbccddeeff, 0x1a2b3c4d5e6f, @@ -64,29 +90,6 @@ static const uint64_t g_mifare_default_keys[] = { 0x0000014b5c31, 0xb578f38a5c61, 0x96a301bce267, - 0xfc00018778f7, // Public Transport - 0x6471a5ef2d1a, // SimonsVoss - 0x4E3552426B32, // ID06 - 0x6A1987C40A21, // Salto - 0xef1232ab18a0, // Schlage - 0x3B7E4FD575AD, // - 0xb7bf0c13066e, // Gallagher - 0x135b88a94b8b, // Saflock - 0x5a7a52d5e20d, // Bosch - 0x314B49474956, // VIGIK1 A - 0x564c505f4d41, // VIGIK1 B - 0x021209197591, // BTCINO - 0x484558414354, // Intratone - 0xEC0A9B1A9E06, // Vingcard - 0x66b31e64ca4b, // Vingcard - 0x97F5DA640B18, // Bangkok metro key - 0xA8844B0BCA06, // Metro Valencia key - 0xE4410EF8ED2D, // Armenian metro - 0x857464D3AAD1, // HTC Eindhoven key - 0x08B386463229, // troika - 0xe00000000000, // icopy - 0x199404281970, // NSP A - 0x199404281998, // NSP B }; static const uint8_t g_mifare_default_key[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; From 757e53c6ac12fce80f4f11f6e89c4bedc88ee870 Mon Sep 17 00:00:00 2001 From: Davi Mikael <31720422+penegui@users.noreply.github.com> Date: Wed, 24 May 2023 09:48:00 -0300 Subject: [PATCH 59/62] removing debug statement in iso14443a --- armsrc/iso14443a.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index 741440a0a..b4730b62d 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -1190,11 +1190,9 @@ bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data, tag_r rATQA[0] &= 0xBF; if(tagType == 11){ - rSAKc1[0] = sak & 0xFC & 0X70; - DbpString(_YELLOW_("[ ") "Passando no Sak Penegui" _YELLOW_(" ]")); + rSAKc1[0] = sak & 0xFC & 0X70; }else{ - rSAKc1[0] = sak & 0xFB; - //DbpString(_YELLOW_("[ ") "Passando no Sak Antigo" _YELLOW_(" ]")); + rSAKc1[0] = sak & 0xFB; } AddCrc14A(rSAKc1, sizeof(rSAKc1) - 2); From 416abae4bb05d250401d102c15fde5eadecbedbb Mon Sep 17 00:00:00 2001 From: Davi Mikael <31720422+penegui@users.noreply.github.com> Date: Wed, 24 May 2023 09:49:40 -0300 Subject: [PATCH 60/62] Changing token and uid of hf_msdsal --- armsrc/Standalone/hf_msdsal.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/armsrc/Standalone/hf_msdsal.c b/armsrc/Standalone/hf_msdsal.c index 64db3a446..b0d81ff79 100644 --- a/armsrc/Standalone/hf_msdsal.c +++ b/armsrc/Standalone/hf_msdsal.c @@ -168,7 +168,7 @@ void RunMod(void) { //Expiration date: 17/11 //Service code: 201 //Discretionary data: 0000030000991 - //char token[19] = {0x44,0x12,0x34,0x56,0x05,0x78,0x56,0x78,0xd1,0x71,0x12,0x01,0x00,0x00,0x03,0x00,0x00,0x99,0x1f}; + //char token[19] = {0x44,0x12,0x34,0x56,0x05,0x78,0x12,0x34,0xd1,0x71,0x12,0x01,0x00,0x00,0x03,0x00,0x00,0x99,0x1f}; // // It is possible to initialize directly the emulation mode, having "token" with data and set "chktoken" = true ;) // @@ -185,7 +185,7 @@ void RunMod(void) { // in case there is a read command received we shouldn't break uint8_t data[PM3_CMD_DATA_SIZE] = {0x00}; - uint8_t visauid[7] = {0x05, 0x06, 0x07, 0x08}; + uint8_t visauid[7] = {0x01, 0x02, 0x03, 0x04}; memcpy(data, visauid, 4); // to initialize the emulation From 37e5540aab52d7b57ed206b65ca71d06435e4bae Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Wed, 24 May 2023 15:43:44 +0200 Subject: [PATCH 61/62] fixed a logic error. param N is used as total number of blocks. I is using it as zero based index... --- client/src/cmdhfmf.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/client/src/cmdhfmf.c b/client/src/cmdhfmf.c index 8d862b78d..ffc607617 100644 --- a/client/src/cmdhfmf.c +++ b/client/src/cmdhfmf.c @@ -304,7 +304,9 @@ static int mf_print_keys(uint16_t n, uint8_t *d) { return PM3_EMALLOC; } - for (uint16_t i = 0; i < n; i++) { + // n is number of blocks, but in the loop its a zero based index. + // n = 20, i = 0-19 + for (uint16_t i = 0; i < n - 1; i++) { if (mfIsSectorTrailer(i)) { e_sector[mfSectorNum(i)].foundKey[0] = 1; e_sector[mfSectorNum(i)].Key[0] = bytes_to_num(d + (i * MFBLOCK_SIZE), MIFARE_KEY_SIZE); From 1b67921086c2be20a3d6cf84ee7ec126707799a9 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Wed, 24 May 2023 17:19:34 +0200 Subject: [PATCH 62/62] renaming and making sure N never is a strange number --- client/src/cmdhf14a.c | 22 ++++++--- client/src/cmdhfmf.c | 112 +++++++++++++++++++++++------------------- client/src/cmdhfmf.h | 9 ++-- 3 files changed, 79 insertions(+), 64 deletions(-) diff --git a/client/src/cmdhf14a.c b/client/src/cmdhf14a.c index e1453be3b..0a9a23578 100644 --- a/client/src/cmdhf14a.c +++ b/client/src/cmdhf14a.c @@ -41,6 +41,7 @@ #include "atrs.h" // getATRinfo #include "desfire.h" // desfire enums #include "mifare/desfirecore.h" // desfire context +#include "mifare/mifaredefault.h" static bool g_apdu_in_framing_enable = true; bool Get_apdu_in_framing(void) { @@ -738,9 +739,6 @@ int CmdHF14ASim(const char *Cmd) { return PM3_EINVARG; } - sector_t *k_sector = NULL; - uint8_t k_sectorsCount = 40; - if (useUIDfromEML) { flags |= FLAG_UID_IN_EMUL; } @@ -761,17 +759,24 @@ int CmdHF14ASim(const char *Cmd) { SendCommandNG(CMD_HF_ISO14443A_SIMULATE, (uint8_t *)&payload, sizeof(payload)); PacketResponseNG resp; + sector_t *k_sector = NULL; + size_t k_sectors_cnt = MIFARE_4K_MAXSECTOR; + PrintAndLogEx(INFO, "Press pm3-button to abort simulation"); bool keypress = kbd_enter_pressed(); while (keypress == false) { - if (WaitForResponseTimeout(CMD_HF_MIFARE_SIMULATE, &resp, 1500) == 0) continue; - if (resp.status != PM3_SUCCESS) break; + if (WaitForResponseTimeout(CMD_HF_MIFARE_SIMULATE, &resp, 1500) == 0) + continue; - if ((flags & FLAG_NR_AR_ATTACK) != FLAG_NR_AR_ATTACK) break; + if (resp.status != PM3_SUCCESS) + break; + + if ((flags & FLAG_NR_AR_ATTACK) != FLAG_NR_AR_ATTACK) + break; nonces_t *data = (nonces_t *)resp.data.asBytes; - readerAttack(k_sector, k_sectorsCount, data[0], setEmulatorMem, verbose); + readerAttack(k_sector, k_sectors_cnt, data[0], setEmulatorMem, verbose); keypress = kbd_enter_pressed(); } @@ -783,7 +788,8 @@ int CmdHF14ASim(const char *Cmd) { } if (resp.status == PM3_EOPABORTED && ((flags & FLAG_NR_AR_ATTACK) == FLAG_NR_AR_ATTACK)) { - showSectorTable(k_sector, k_sectorsCount); + //iceman: readerAttack call frees k_sector , this call is useless. + showSectorTable(k_sector, k_sectors_cnt); } } diff --git a/client/src/cmdhfmf.c b/client/src/cmdhfmf.c index ffc607617..12799b25f 100644 --- a/client/src/cmdhfmf.c +++ b/client/src/cmdhfmf.c @@ -141,8 +141,8 @@ static int initSectorTable(sector_t **src, size_t items) { return PM3_EMALLOC; // empty e_sector - for (int i = 0; i < items; i++) { - for (int j = 0; j < 2; j++) { + for (size_t i = 0; i < items; i++) { + for (uint8_t j = 0; j < 2; j++) { (*src)[i].Key[j] = 0xffffffffffff; (*src)[i].foundKey[j] = 0; } @@ -294,19 +294,21 @@ static int mf_print_keys(uint16_t n, uint8_t *d) { sectors = MIFARE_4K_MAXSECTOR; break; case MIFARE_1K_MAXBLOCK: + sectors = MIFARE_1K_MAXSECTOR; + break; default: sectors = MIFARE_1K_MAXSECTOR; + n = MIFARE_1K_MAXBLOCK; break; } sector_t *e_sector = calloc(sectors, sizeof(sector_t)); - if (e_sector == NULL) { + if (e_sector == NULL) { return PM3_EMALLOC; } - // n is number of blocks, but in the loop its a zero based index. - // n = 20, i = 0-19 - for (uint16_t i = 0; i < n - 1; i++) { + for (uint16_t i = 0; i < n; i++) { + if (mfIsSectorTrailer(i)) { e_sector[mfSectorNum(i)].foundKey[0] = 1; e_sector[mfSectorNum(i)].Key[0] = bytes_to_num(d + (i * MFBLOCK_SIZE), MIFARE_KEY_SIZE); @@ -2380,7 +2382,7 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { // create/initialize key storage structure sector_t *e_sector = NULL; - uint32_t e_sector_cnt = (sector_cnt > sectorno) ? sector_cnt : sectorno + 1; + size_t e_sector_cnt = (sector_cnt > sectorno) ? sector_cnt : sectorno + 1; if (initSectorTable(&e_sector, e_sector_cnt) != PM3_SUCCESS) { return PM3_EMALLOC; } @@ -3151,7 +3153,7 @@ static int CmdHF14AMfChk_fast(const char *Cmd) { m1 = true; } - uint8_t sectorsCnt = 1; + uint8_t sectorsCnt = MIFARE_1K_MAXSECTOR; if (m0) { sectorsCnt = MIFARE_MINI_MAXSECTOR; } else if (m1) { @@ -3367,42 +3369,42 @@ static int CmdHF14AMfChk(const char *Cmd) { return PM3_EINVARG; } - uint8_t SectorsCnt = 1; + size_t sectors_cnt = 1; if (m0) { - SectorsCnt = MIFARE_MINI_MAXSECTOR; + sectors_cnt = MIFARE_MINI_MAXSECTOR; } else if (m1) { - SectorsCnt = MIFARE_1K_MAXSECTOR; + sectors_cnt = MIFARE_1K_MAXSECTOR; } else if (m2) { - SectorsCnt = MIFARE_2K_MAXSECTOR; + sectors_cnt = MIFARE_2K_MAXSECTOR; } else if (m4) { - SectorsCnt = MIFARE_4K_MAXSECTOR; + sectors_cnt = MIFARE_4K_MAXSECTOR; } if (singleSector) { - uint8_t MinSectorsCnt = 0; + size_t min_sectors_cnt = 0; // find a MIFARE type that can accommodate the provided block number uint8_t s = mfSectorNum(blockNo); if (s < MIFARE_MINI_MAXSECTOR) { - MinSectorsCnt = MIFARE_MINI_MAXSECTOR; + min_sectors_cnt = MIFARE_MINI_MAXSECTOR; } else if (s < MIFARE_1K_MAXSECTOR) { - MinSectorsCnt = MIFARE_1K_MAXSECTOR; + min_sectors_cnt = MIFARE_1K_MAXSECTOR; } else if (s < MIFARE_2K_MAXSECTOR) { - MinSectorsCnt = MIFARE_2K_MAXSECTOR; + min_sectors_cnt = MIFARE_2K_MAXSECTOR; } else if (s < MIFARE_4K_MAXSECTOR) { - MinSectorsCnt = MIFARE_4K_MAXSECTOR; + min_sectors_cnt = MIFARE_4K_MAXSECTOR; } else { PrintAndLogEx(WARNING, "Provided block out of possible MIFARE Type memory map"); return PM3_EINVARG; } - if (SectorsCnt == 1) { - SectorsCnt = MinSectorsCnt; - } else if (SectorsCnt < MinSectorsCnt) { + if (sectors_cnt == 1) { + sectors_cnt = min_sectors_cnt; + } else if (sectors_cnt < min_sectors_cnt) { PrintAndLogEx(WARNING, "Provided block out of provided MIFARE Type memory map"); return PM3_EINVARG; } } - if (SectorsCnt == 1) { - SectorsCnt = MIFARE_1K_MAXSECTOR; + if (sectors_cnt == 1) { + sectors_cnt = MIFARE_1K_MAXSECTOR; } uint8_t *keyBlock = NULL; @@ -3416,7 +3418,7 @@ static int CmdHF14AMfChk(const char *Cmd) { // create/initialize key storage structure sector_t *e_sector = NULL; - if (initSectorTable(&e_sector, SectorsCnt) != PM3_SUCCESS) { + if (initSectorTable(&e_sector, sectors_cnt) != PM3_SUCCESS) { free(keyBlock); return PM3_EMALLOC; } @@ -3441,7 +3443,7 @@ static int CmdHF14AMfChk(const char *Cmd) { // loop sectors but block is used as to keep track of from which blocks to test int b = blockNo; - for (int i = mfSectorNum(b); i < SectorsCnt; ++i) { + for (int i = mfSectorNum(b); i < sectors_cnt; ++i) { // skip already found keys. if (e_sector[i].foundKey[trgKeyType]) continue; @@ -3480,7 +3482,7 @@ static int CmdHF14AMfChk(const char *Cmd) { // loop sectors but block is used as to keep track of from which blocks to test int b = blockNo; - for (int i = mfSectorNum(b); i < SectorsCnt; i++) { + for (int i = mfSectorNum(b); i < sectors_cnt; i++) { // KEY A but not KEY B if (e_sector[i].foundKey[0] && !e_sector[i].foundKey[1]) { @@ -3526,13 +3528,13 @@ out: if (singleSector) printKeyTableEx(1, e_sector, mfSectorNum(blockNo)); else - printKeyTable(SectorsCnt, e_sector); + printKeyTable(sectors_cnt, e_sector); if (transferToEml) { // fast push mode g_conn.block_after_ACK = true; uint8_t block[16] = {0x00}; - for (int i = 0; i < SectorsCnt; ++i) { + for (int i = 0; i < sectors_cnt; ++i) { uint8_t blockno = mfFirstBlockOfSector(i) + mfNumBlocksPerSector(i) - 1; mfEmlGetMem(block, blockno, 1); @@ -3542,7 +3544,7 @@ out: if (e_sector[i].foundKey[1]) num_to_bytes(e_sector[i].Key[1], 6, block + 10); - if (i == SectorsCnt - 1) { + if (i == sectors_cnt - 1) { // Disable fast mode on last packet g_conn.block_after_ACK = false; } @@ -3553,7 +3555,7 @@ out: if (createDumpFile) { char *fptr = GenerateFilename("hf-mf-", "-key.bin"); - if (createMfcKeyDump(fptr, SectorsCnt, e_sector) != PM3_SUCCESS) { + if (createMfcKeyDump(fptr, sectors_cnt, e_sector) != PM3_SUCCESS) { PrintAndLogEx(ERR, "Failed to save keys to file"); } free(fptr); @@ -3574,14 +3576,14 @@ out: return PM3_SUCCESS; } -void showSectorTable(sector_t *k_sector, uint8_t k_sectors_cnt) { +void showSectorTable(sector_t *k_sector, size_t k_sectors_cnt) { if (k_sector != NULL) { printKeyTable(k_sectors_cnt, k_sector); free(k_sector); } } -void readerAttack(sector_t *k_sector, uint8_t k_sectors_cnt, nonces_t data, bool setEmulatorMem, bool verbose) { +void readerAttack(sector_t *k_sector, size_t k_sectors_cnt, nonces_t data, bool setEmulatorMem, bool verbose) { // init if needed if (k_sector == NULL) { @@ -3715,22 +3717,19 @@ static int CmdHF14AMfSim(const char *Cmd) { } CLIParserFree(ctx); - sector_t *k_sector = NULL; - //Validations if (atqalen > 0) { if (atqalen != 2) { PrintAndLogEx(WARNING, "Wrong ATQA length"); return PM3_EINVARG; - } flags |= FLAG_FORCED_ATQA; } + if (saklen > 0) { if (saklen != 1) { PrintAndLogEx(WARNING, "Wrong SAK length"); return PM3_EINVARG; - } flags |= FLAG_FORCED_SAK; } @@ -3740,7 +3739,7 @@ static int CmdHF14AMfSim(const char *Cmd) { flags |= FLAG_UID_IN_EMUL; } - uint8_t k_sectorsCount = 40; + size_t k_sectors_cnt = MIFARE_4K_MAXSECTOR; char csize[13] = { 0 }; if ((m0 + m1 + m2 + m4) > 1) { @@ -3751,19 +3750,19 @@ static int CmdHF14AMfSim(const char *Cmd) { if (m0) { flags |= FLAG_MF_MINI; snprintf(csize, sizeof(csize), "MINI"); - k_sectorsCount = MIFARE_MINI_MAXSECTOR; + k_sectors_cnt = MIFARE_MINI_MAXSECTOR; } else if (m1) { flags |= FLAG_MF_1K; snprintf(csize, sizeof(csize), "1K"); - k_sectorsCount = MIFARE_1K_MAXSECTOR; + k_sectors_cnt = MIFARE_1K_MAXSECTOR; } else if (m2) { flags |= FLAG_MF_2K; snprintf(csize, sizeof(csize), "2K with RATS"); - k_sectorsCount = MIFARE_2K_MAXSECTOR; + k_sectors_cnt = MIFARE_2K_MAXSECTOR; } else if (m4) { flags |= FLAG_MF_4K; snprintf(csize, sizeof(csize), "4K"); - k_sectorsCount = MIFARE_4K_MAXSECTOR; + k_sectors_cnt = MIFARE_4K_MAXSECTOR; } else { PrintAndLogEx(WARNING, "Please specify a MIFARE Type"); return PM3_EINVARG; @@ -3801,15 +3800,26 @@ static int CmdHF14AMfSim(const char *Cmd) { if (flags & FLAG_INTERACTIVE) { PrintAndLogEx(INFO, "Press pm3-button or send another cmd to abort simulation"); - while (!kbd_enter_pressed()) { - if (!WaitForResponseTimeout(CMD_ACK, &resp, 1500)) continue; - if (!(flags & FLAG_NR_AR_ATTACK)) break; - if ((resp.oldarg[0] & 0xffff) != CMD_HF_MIFARE_SIMULATE) break; + sector_t *k_sector = NULL; + + while (kbd_enter_pressed() == 0) { + + if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) + continue; + + if ((flags & FLAG_NR_AR_ATTACK) != FLAG_NR_AR_ATTACK) + break; + + if ((resp.oldarg[0] & 0xffff) != CMD_HF_MIFARE_SIMULATE) + break; + nonces_t data[1]; memcpy(data, resp.data.asBytes, sizeof(data)); - readerAttack(k_sector, k_sectorsCount, data[0], setEmulatorMem, verbose); + readerAttack(k_sector, k_sectors_cnt, data[0], setEmulatorMem, verbose); } - showSectorTable(k_sector, k_sectorsCount); + //iceman: readerAttack call frees k_sector. this call below is useless. + showSectorTable(k_sector, k_sectors_cnt); + } else { PrintAndLogEx(INFO, "Press pm3-button to abort simulation"); } @@ -3849,11 +3859,11 @@ static int CmdHF14AMfKeyBrute(const char *Cmd) { } */ -void printKeyTable(uint8_t sectorscnt, sector_t *e_sector) { +void printKeyTable(size_t sectorscnt, sector_t *e_sector) { return printKeyTableEx(sectorscnt, e_sector, 0); } -void printKeyTableEx(uint8_t sectorscnt, sector_t *e_sector, uint8_t start_sector) { +void printKeyTableEx(size_t sectorscnt, sector_t *e_sector, uint8_t start_sector) { char strA[26 + 1] = {0}; char strB[26 + 1] = {0}; char resA[20 + 1] = {0}; @@ -3867,7 +3877,7 @@ void printKeyTableEx(uint8_t sectorscnt, sector_t *e_sector, uint8_t start_secto uint64_t ndef_key = 0xD3F7D3F7D3F7; bool has_ndef_key = false; bool extended_legend = false; - for (uint8_t i = 0; i < sectorscnt; i++) { + for (size_t i = 0; i < sectorscnt; i++) { if ((e_sector[i].foundKey[0] > 1) || (e_sector[i].foundKey[1] > 1)) { extended_legend = true; @@ -4545,7 +4555,7 @@ static int CmdHF14AMfEKeyPrn(const char *Cmd) { m1 = true; } - uint8_t sectors_cnt = MIFARE_1K_MAXSECTOR; + size_t sectors_cnt = MIFARE_1K_MAXSECTOR; if (m0) { sectors_cnt = MIFARE_MINI_MAXSECTOR; diff --git a/client/src/cmdhfmf.h b/client/src/cmdhfmf.h index 62b95de4c..5c21c7fb3 100644 --- a/client/src/cmdhfmf.h +++ b/client/src/cmdhfmf.h @@ -30,11 +30,10 @@ int CmdHFMFNDEFRead(const char *Cmd); // used by "nfc mf cread" int CmdHFMFNDEFFormat(const char *Cmd); // used by "nfc mf cformat" int CmdHFMFNDEFWrite(const char *Cmd); // used by "nfc mf cwrite" -void showSectorTable(sector_t *k_sector, uint8_t k_sectors_cnt); -void readerAttack(sector_t *k_sector, uint8_t k_sectors_cnt, nonces_t data, bool setEmulatorMem, bool verbose); -void printKeyTable(uint8_t sectorscnt, sector_t *e_sector); -void printKeyTableEx(uint8_t sectorscnt, sector_t *e_sector, uint8_t start_sector); -void printKeyTable_fast(uint8_t sectorscnt, icesector_t *e_sector, uint64_t bar, uint64_t foo); +void showSectorTable(sector_t *k_sector, size_t k_sectors_cnt); +void readerAttack(sector_t *k_sector, size_t k_sectors_cnt, nonces_t data, bool setEmulatorMem, bool verbose); +void printKeyTable(size_t sectorscnt, sector_t *e_sector); +void printKeyTableEx(size_t sectorscnt, sector_t *e_sector, uint8_t start_sector); int mfc_ev1_print_signature(uint8_t *uid, uint8_t uidlen, uint8_t *signature, int signature_len); #endif