From 43907dc4ed99c1af83154c9348293a06199df08b Mon Sep 17 00:00:00 2001 From: mwalker33 Date: Mon, 12 Oct 2020 15:33:14 +1100 Subject: [PATCH 01/58] Update cmddata.c Fix issues when char cast to int8_t --- client/src/cmddata.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/client/src/cmddata.c b/client/src/cmddata.c index bb831f311..4f5c55363 100644 --- a/client/src/cmddata.c +++ b/client/src/cmddata.c @@ -1949,7 +1949,7 @@ int CmdSave(const char *Cmd) { void *argtable[] = { arg_param_begin, arg_lit0("w", "wave", "save as wave format (.wav)"), - arg_strx0("f", "file", "", "save file name"), + arg_str1("f", "file", "", "save file name"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -1958,7 +1958,9 @@ int CmdSave(const char *Cmd) { int fnlen = 0; char filename[FILE_PATH_SIZE] = {0}; - CLIGetStrWithReturn(ctx, 2, (uint8_t *)filename, &fnlen); + // CLIGetStrWithReturn(ctx, 2, (uint8_t *)filename, &fnlen); + CLIParamStrToBuf(arg_get_str(ctx, 2), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); + CLIParserFree(ctx); if (as_wave) From e9087175a2a5a8ce5ba8ad3631b5e71ffa005066 Mon Sep 17 00:00:00 2001 From: Uli Heilmeier Date: Mon, 12 Oct 2020 11:25:32 +0200 Subject: [PATCH 02/58] cmdhflegic: Make sure to show help text Show usage when parameter h is used or none parameter --- client/src/cmdhflegic.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/client/src/cmdhflegic.c b/client/src/cmdhflegic.c index 481d1f1bc..39a542cc9 100644 --- a/client/src/cmdhflegic.c +++ b/client/src/cmdhflegic.c @@ -629,6 +629,10 @@ static int CmdLegicWrbl(const char *Cmd) { while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { switch (tolower(param_getchar(Cmd, cmdp))) { + case 'h': { + errors = true; + break; + } case 'd': { // peek at length of the input string so we can // figure out how many elements to malloc in "data" @@ -679,10 +683,6 @@ static int CmdLegicWrbl(const char *Cmd) { cmdp += 2; break; } - case 'h': { - errors = true; - break; - } case 'y': { autoconfirm = true; break; @@ -695,6 +695,13 @@ static int CmdLegicWrbl(const char *Cmd) { } } + //Validations + if (errors || cmdp == 0) { + if (data) + free(data); + return usage_legic_wrbl(); + } + // OUT-OF-BOUNDS checks // UID 4+1 bytes can't be written to. if (offset < 5) { @@ -704,13 +711,6 @@ static int CmdLegicWrbl(const char *Cmd) { return PM3_EOUTOFBOUND; } - //Validations - if (errors || cmdp == 0) { - if (data) - free(data); - return usage_legic_wrbl(); - } - // tagtype legic_card_select_t card; if (legic_get_type(&card) != PM3_SUCCESS) { From a9400bed51f23810b8bee38daa14e0145806262f Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Mon, 12 Oct 2020 13:08:34 +0200 Subject: [PATCH 03/58] Q5 - wrong setting --- client/src/cmdlfguard.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/cmdlfguard.c b/client/src/cmdlfguard.c index 1ab8df3d8..75ecb55bb 100644 --- a/client/src/cmdlfguard.c +++ b/client/src/cmdlfguard.c @@ -186,7 +186,7 @@ static int CmdGuardClone(const char *Cmd) { // Q5 bool q5 = tolower(param_getchar(Cmd, 3)) == 'q'; if (q5) - blocks[0] = T5555_FIXED | T5555_MODULATION_FSK2 | T5555_SET_BITRATE(50) | 3 << T5555_MAXBLOCK_SHIFT; + blocks[0] = T5555_FIXED | T5555_MODULATION_BIPHASE | T5555_SET_BITRATE(64) | 3 << T5555_MAXBLOCK_SHIFT; blocks[1] = bytebits_to_byte(bs, 32); blocks[2] = bytebits_to_byte(bs + 32, 32); From cb0ad464c15530cfebc8b235084498be66c5c463 Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Mon, 12 Oct 2020 13:39:57 +0200 Subject: [PATCH 04/58] Fix T55xx modulation parser --- client/src/cmdlft55xx.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client/src/cmdlft55xx.h b/client/src/cmdlft55xx.h index e17e1ac24..4352eed64 100644 --- a/client/src/cmdlft55xx.h +++ b/client/src/cmdlft55xx.h @@ -110,9 +110,9 @@ typedef enum { DEMOD_PSK1 = 0x01, DEMOD_PSK2 = 0x02, DEMOD_PSK3 = 0x03, - DEMOD_FSK1 = 0x04, - DEMOD_FSK1a = 0x05, - DEMOD_FSK2 = 0x06, + DEMOD_FSK1 = 0x04, + DEMOD_FSK2 = 0x05, + DEMOD_FSK1a = 0x06, DEMOD_FSK2a = 0x07, DEMOD_FSK = 0xF0, //generic FSK (auto detect FCs) DEMOD_ASK = 0x08, From 2ee9ea0ef3f568aef2e09676ee216a636aa990ae Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Mon, 12 Oct 2020 12:27:00 +0200 Subject: [PATCH 05/58] Add Destron --- client/CMakeLists.txt | 1 + client/Makefile | 1 + client/android/CMakeLists.txt | 1 + client/src/cmdlf.c | 3 + client/src/cmdlfdestron.c | 189 ++++++++++++++++++++++++++++++++++ client/src/cmdlfdestron.h | 19 ++++ client/src/cmdlft55xx.c | 2 +- tools/pm3_tests.sh | 1 + 8 files changed, 216 insertions(+), 1 deletion(-) create mode 100644 client/src/cmdlfdestron.c create mode 100644 client/src/cmdlfdestron.h diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index c371ecb29..e7262ba33 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -246,6 +246,7 @@ set (TARGET_SOURCES ${PM3_ROOT}/client/src/cmdlf.c ${PM3_ROOT}/client/src/cmdlfawid.c ${PM3_ROOT}/client/src/cmdlfcotag.c + ${PM3_ROOT}/client/src/cmdlfdestron.c ${PM3_ROOT}/client/src/cmdlfem4x.c ${PM3_ROOT}/client/src/cmdlfem4x50.c ${PM3_ROOT}/client/src/cmdlffdxb.c diff --git a/client/Makefile b/client/Makefile index 9fa8565e6..27702a9d6 100644 --- a/client/Makefile +++ b/client/Makefile @@ -441,6 +441,7 @@ SRCS = aidsearch.c \ cmdlf.c \ cmdlfawid.c \ cmdlfcotag.c \ + cmdlfdestron.c \ cmdlfem4x.c \ cmdlfem4x50.c \ cmdlffdxb.c \ diff --git a/client/android/CMakeLists.txt b/client/android/CMakeLists.txt index 3ab423c3c..b3f432c6c 100644 --- a/client/android/CMakeLists.txt +++ b/client/android/CMakeLists.txt @@ -125,6 +125,7 @@ add_library(pm3rrg_rdv4 SHARED ${PM3_ROOT}/client/src/cmdlf.c ${PM3_ROOT}/client/src/cmdlfawid.c ${PM3_ROOT}/client/src/cmdlfcotag.c + ${PM3_ROOT}/client/src/cmdlfdestron.c ${PM3_ROOT}/client/src/cmdlfem4x.c ${PM3_ROOT}/client/src/cmdlfem4x50.c ${PM3_ROOT}/client/src/cmdlffdxb.c diff --git a/client/src/cmdlf.c b/client/src/cmdlf.c index e42fd777c..d278d4c9c 100644 --- a/client/src/cmdlf.c +++ b/client/src/cmdlf.c @@ -36,6 +36,7 @@ #include "cmdlfidteck.h" // for idteck menu #include "cmdlfio.h" // for ioprox menu #include "cmdlfcotag.h" // for COTAG meny +#include "cmdlfdestron.h" // for FDX-A FECAVA Destron menu #include "cmdlffdxb.h" // for FDX-B menu #include "cmdlfgallagher.h" // for GALLAGHER menu #include "cmdlfguard.h" // for gproxii menu @@ -1447,6 +1448,7 @@ int CmdLFfind(const char *Cmd) { } if (demodVisa2k(true) == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("Visa2000 ID") " found!"); goto out;} + if (demodDestron(true) == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("FDX-A FECAVA Destron ID") " found!"); goto out;} // to do before HID if (demodHID(true) == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("HID Prox ID") " found!"); goto out;} if (demodAWID(true) == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("AWID ID") " found!"); goto out;} if (demodIOProx(true) == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("IO Prox ID") " found!"); goto out;} @@ -1526,6 +1528,7 @@ static command_t CommandTable[] = { {"-----------", CmdHelp, AlwaysAvailable, "-------------- " _CYAN_("Direct") " --------------"}, {"awid", CmdLFAWID, AlwaysAvailable, "{ AWID RFIDs... }"}, {"cotag", CmdLFCOTAG, AlwaysAvailable, "{ COTAG CHIPs... }"}, + {"destron", CmdLFDestron, AlwaysAvailable, "{ FDX-A Destron RFIDs... }"}, {"em", CmdLFEM4X, AlwaysAvailable, "{ EM4X CHIPs & RFIDs... }"}, {"fdxb", CmdLFFdxB, AlwaysAvailable, "{ FDX-B RFIDs... }"}, {"gallagher", CmdLFGallagher, AlwaysAvailable, "{ GALLAGHER RFIDs... }"}, diff --git a/client/src/cmdlfdestron.c b/client/src/cmdlfdestron.c new file mode 100644 index 000000000..148557a80 --- /dev/null +++ b/client/src/cmdlfdestron.c @@ -0,0 +1,189 @@ +//----------------------------------------------------------------------------- +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// Low frequency FDX-A FECAVA Destron tag commands +//----------------------------------------------------------------------------- +#include "cmdlfdestron.h" + +#include //tolower +#include // memcpy +#include "commonutil.h" // ARRAYLEN +#include "common.h" +#include "cmdparser.h" // command_t +#include "comms.h" +#include "ui.h" +#include "cmddata.h" +#include "cmdlf.h" +#include "lfdemod.h" // preamble test +#include "protocols.h" // t55xx defines +#include "cmdlft55xx.h" // clone.. +#include "cmdlf.h" // cmdlfconfig +#include "cliparser.h" // cli parse input +#include "parity.h" + +#define DESTRON_FRAME_SIZE 96 +#define DESTRON_PREAMBLE_SIZE 16 + +static int CmdHelp(const char *Cmd); + +int demodDestron(bool verbose) { + (void) verbose; // unused so far + //PSK1 + if (FSKrawDemod(0, 0, 0, 0, false) != PM3_SUCCESS) { + PrintAndLogEx(DEBUG, "DEBUG: Error - Destron: FSK Demod failed"); + return PM3_ESOFT; + } + size_t size = DemodBufferLen; + int ans = detectDestron(DemodBuffer, &size); + if (ans < 0) { + if (ans == -1) + PrintAndLogEx(DEBUG, "DEBUG: Error - Destron: too few bits found"); + else if (ans == -2) + PrintAndLogEx(DEBUG, "DEBUG: Error - Destron: preamble not found"); + else if (ans == -3) + PrintAndLogEx(DEBUG, "DEBUG: Error - Destron: Size not correct: %zu", size); + else + PrintAndLogEx(DEBUG, "DEBUG: Error - Destron: ans: %d", ans); + + return PM3_ESOFT; + } + setDemodBuff(DemodBuffer, DESTRON_FRAME_SIZE, ans); + setClockGrid(g_DemodClock, g_DemodStartIdx + (ans * g_DemodClock)); + + uint8_t bits[DESTRON_FRAME_SIZE - DESTRON_PREAMBLE_SIZE] = {0}; + size_t bitlen = DESTRON_FRAME_SIZE - DESTRON_PREAMBLE_SIZE; + memcpy(bits, DemodBuffer + 16, DESTRON_FRAME_SIZE - DESTRON_PREAMBLE_SIZE); + + uint8_t alignPos = 0; + uint16_t errCnt = manrawdecode(bits, &bitlen, 0, &alignPos); + if (errCnt > 0) { + PrintAndLogEx(DEBUG, "DEBUG: Error - Destron: Manchester decoding errors: %d", ans); + return PM3_ESOFT; + } + + uint8_t data[5] = {0}; + uint8_t parity_err = 0; + for (int i=0; i < sizeof(data); i++) { + data[i] = bytebits_to_byte(bits + i * 8, 8); + parity_err += oddparity8(data[i]); + data[i] &= 0x7F; + } + if (errCnt > 0) { + PrintAndLogEx(DEBUG, "DEBUG: Error - Destron: parity errors: %d", parity_err); + return PM3_ESOFT; + } + PrintAndLogEx(SUCCESS, "FDX-A FECAVA Destron: " _GREEN_("%02X%02X%02X%02X%02X"), data[0], data[1], data[2], data[3], data[4]); + return PM3_SUCCESS; +} + +static int CmdDestronDemod(const char *Cmd) { + (void)Cmd; + return demodDestron(true); +} + +static int CmdDestronRead(const char *Cmd) { + lf_read(false, 16000); + return demodDestron(true); +} + +static int CmdDestronClone(const char *Cmd) { + + uint32_t blocks[4] = {0}; + uint8_t data[8]; + int datalen = 0; + + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf destron clone", + "Enables cloning of Destron card with specified uid onto T55x7", + "lf destron clone 1A2B3C4D5E" + ); + + void *argtable[] = { + arg_param_begin, + arg_strx1(NULL, NULL, "", NULL), + arg_param_end + }; + + //TODO add selection of chip for Q5 or T55x7 + CLIExecWithReturn(ctx, Cmd, argtable, false); + CLIGetHexWithReturn(ctx, 1, data, &datalen); + CLIParserFree(ctx); + + uint8_t data_ex[12 + 24] = {0}; // ManchesterEncode need extra room + for (int i=0; i < datalen; i++) { + data_ex[i + 1] = data [i] | (oddparity8(data[i]) << 7); + } + for (int i=0; i < 3; i++) { + blocks[i+1] = manchesterEncode2Bytes((data_ex[i*2]<<8)+data_ex[i*2+1]); + } + // inject preamble + blocks[1] = (blocks[1] & 0xFFFF) | 0xAAE20000; + + PrintAndLogEx(INFO, "Preparing to clone Destron tag with ID: %s", sprint_hex(data, datalen)); + blocks[0] = T55x7_BITRATE_RF_50 | T55x7_MODULATION_FSK2 | 3 << T55x7_MAXBLOCK_SHIFT; + + print_blocks(blocks, ARRAYLEN(blocks)); + int res = clone_t55xx_tag(blocks, ARRAYLEN(blocks)); + PrintAndLogEx(SUCCESS, "Done"); + PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf Destron read`") " to verify"); + return res; +} + +static int CmdDestronSim(const char *Cmd) { + + PrintAndLogEx(INFO, " To be implemented, feel free to contribute!"); + return PM3_SUCCESS; +} + +static command_t CommandTable[] = { + {"help", CmdHelp, AlwaysAvailable, "This help"}, + {"demod", CmdDestronDemod, AlwaysAvailable, "Demodulate an Destron tag from the GraphBuffer"}, + {"read", CmdDestronRead, IfPm3Lf, "Attempt to read and extract tag data from the antenna"}, + {"clone", CmdDestronClone, IfPm3Lf, "Clone Destron tag to T55x7"}, + {"sim", CmdDestronSim, IfPm3Lf, "Simulate Destron tag"}, + {NULL, NULL, NULL, NULL} +}; + +static int CmdHelp(const char *Cmd) { + (void)Cmd; // Cmd is not used so far + CmdsHelp(CommandTable); + return PM3_SUCCESS; +} + +int CmdLFDestron(const char *Cmd) { + clearCommandBuffer(); + return CmdsParse(CommandTable, Cmd); +} + +// find Destron preamble in already demoded data +int detectDestron(uint8_t *dest, size_t *size) { + + //make sure buffer has data + if (*size < 64) + return -1; + + size_t found_size = *size; + size_t start_idx = 0; + + uint8_t preamble[DESTRON_PREAMBLE_SIZE] = {1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0}; + + // preamble not found + if (!preambleSearch(dest, preamble, sizeof(preamble), &found_size, &start_idx)) { + return -2; + } + PrintAndLogEx(DEBUG, "DEBUG: detectDestron FSK found preamble"); + + *size = found_size; + // wrong demoded size + if (*size != 96) + return -3; + + return (int)start_idx; +} + +int readDestronUid(void) { + return (CmdDestronRead("") == PM3_SUCCESS); +} diff --git a/client/src/cmdlfdestron.h b/client/src/cmdlfdestron.h new file mode 100644 index 000000000..fb2b62f4a --- /dev/null +++ b/client/src/cmdlfdestron.h @@ -0,0 +1,19 @@ +//----------------------------------------------------------------------------- +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// Low frequency FDX-A FECAVA Destron tag commands +//----------------------------------------------------------------------------- +#ifndef CMDLFDESTRON_H__ +#define CMDLFDESTRON_H__ + +#include "common.h" + +int CmdLFDestron(const char *Cmd); +int detectDestron(uint8_t *bits, size_t *size); +int demodDestron(bool verbose); +int readDestronUid(void); +#endif + diff --git a/client/src/cmdlft55xx.c b/client/src/cmdlft55xx.c index 919debf4c..7c8f39b3b 100644 --- a/client/src/cmdlft55xx.c +++ b/client/src/cmdlft55xx.c @@ -378,7 +378,7 @@ static int usage_t55xx_dangerraw(void) { static int usage_t55xx_clonehelp(void) { PrintAndLogEx(NORMAL, "For cloning specific techs on T55xx tags, see commands available in corresponding LF sub-menus, e.g.:"); PrintAndLogEx(NORMAL, _GREEN_("lf awid clone")); -// todo: rename to clone + PrintAndLogEx(NORMAL, _GREEN_("lf destron clone")); PrintAndLogEx(NORMAL, _GREEN_("lf em 410x_clone")); // todo: implement restore // PrintAndLogEx(NORMAL, _GREEN_("lf em 4x05_write")); diff --git a/tools/pm3_tests.sh b/tools/pm3_tests.sh index e411e667f..4a1293cf8 100755 --- a/tools/pm3_tests.sh +++ b/tools/pm3_tests.sh @@ -341,6 +341,7 @@ while true; do 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 if ! CheckExecute "lf EM410x test" "$CLIENTBIN -c 'data load -f traces/lf_EM4102-1.pm3;lf search 1'" "EM410x ID found"; then break; fi if ! CheckExecute "lf EM4x05 test" "$CLIENTBIN -c 'data load -f traces/lf_EM4x05.pm3;lf search 1'" "FDX-B ID found"; then break; fi + if ! CheckExecute "lf FDX-A FECAVA test" "$CLIENTBIN -c 'data load -f traces/lf_EM4305_fdxa_destron.pm3;lf search 1'" "FDX-A FECAVA Destron ID found"; then break; fi if ! CheckExecute "lf FDX-B test" "$CLIENTBIN -c 'data load -f traces/lf_HomeAgain1600.pm3;lf search 1'" "FDX-B ID found"; then break; fi if ! CheckExecute "lf FDX/BioThermo test" "$CLIENTBIN -c 'data load -f traces/lf_FDXB_Bio-Thermo.pm3; lf fdxb demod'" "95.2 F / 35.1 C"; then break; fi if ! CheckExecute "lf GPROXII test" "$CLIENTBIN -c 'data load -f traces/lf_GProx_36_30_14489.pm3; lf search 1'" "Guardall G-Prox II ID found"; then break; fi From 25195b31970dd20d55f97ea4eaa00af184301c6e Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Mon, 12 Oct 2020 13:57:50 +0200 Subject: [PATCH 06/58] fix destron clone command --- client/src/cmdlfdestron.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/cmdlfdestron.c b/client/src/cmdlfdestron.c index 148557a80..635f2b4f9 100644 --- a/client/src/cmdlfdestron.c +++ b/client/src/cmdlfdestron.c @@ -114,7 +114,7 @@ static int CmdDestronClone(const char *Cmd) { uint8_t data_ex[12 + 24] = {0}; // ManchesterEncode need extra room for (int i=0; i < datalen; i++) { - data_ex[i + 1] = data [i] | (oddparity8(data[i]) << 7); + data_ex[i + 1] = ~data [i] | (evenparity8(data[i]) << 7); } for (int i=0; i < 3; i++) { blocks[i+1] = manchesterEncode2Bytes((data_ex[i*2]<<8)+data_ex[i*2+1]); From b823e8648ac93360cf2877903c50729ecdeebbf8 Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Mon, 12 Oct 2020 14:22:55 +0200 Subject: [PATCH 07/58] Destron: missing trace --- traces/lf_EM4305_fdxa_destron.pm3 | 16000 ++++++++++++++++++++++++++++ 1 file changed, 16000 insertions(+) create mode 100644 traces/lf_EM4305_fdxa_destron.pm3 diff --git a/traces/lf_EM4305_fdxa_destron.pm3 b/traces/lf_EM4305_fdxa_destron.pm3 new file mode 100644 index 000000000..73f4ee475 --- /dev/null +++ b/traces/lf_EM4305_fdxa_destron.pm3 @@ -0,0 +1,16000 @@ +-38 +-66 +-84 +-62 +-7 +32 +61 +79 +81 +4 +-38 +-66 +-84 +-62 +-7 +32 +61 +79 +81 +4 +-38 +-66 +-83 +-62 +-7 +32 +61 +79 +81 +5 +-37 +-66 +-83 +-62 +-6 +33 +61 +80 +82 +4 +-37 +-66 +-83 +-61 +-6 +33 +61 +69 +-2 +-41 +-65 +-49 +3 +40 +66 +72 +-1 +-40 +-65 +-49 +2 +39 +65 +71 +-2 +-41 +-66 +-50 +2 +39 +65 +70 +-2 +-41 +-65 +-49 +2 +39 +65 +71 +-2 +-41 +-66 +-50 +2 +39 +64 +70 +-2 +-41 +-66 +-50 +2 +39 +64 +81 +81 +4 +-39 +-67 +-85 +-63 +-7 +32 +60 +78 +81 +4 +-38 +-66 +-84 +-61 +-6 +33 +61 +79 +81 +5 +-37 +-65 +-82 +-61 +-5 +34 +62 +81 +82 +5 +-37 +-65 +-82 +-61 +-5 +34 +62 +80 +82 +5 +-37 +-65 +-82 +-61 +-5 +34 +62 +70 +-1 +-39 +-64 +-47 +5 +41 +67 +72 +0 +-40 +-65 +-49 +3 +40 +66 +71 +-1 +-40 +-66 +-49 +3 +39 +65 +71 +-2 +-41 +-66 +-49 +2 +39 +64 +70 +-3 +-42 +-66 +-50 +1 +38 +64 +70 +-2 +-42 +-67 +-50 +2 +38 +64 +70 +-2 +-41 +-66 +-82 +-59 +-3 +37 +65 +84 +85 +9 +-34 +-61 +-80 +-58 +-3 +37 +64 +83 +84 +8 +-35 +-62 +-80 +-58 +-3 +36 +64 +82 +83 +7 +-36 +-63 +-81 +-60 +-5 +35 +62 +81 +82 +6 +-37 +-64 +-82 +-60 +-5 +35 +62 +81 +82 +6 +-37 +-64 +-50 +1 +36 +62 +67 +-5 +-45 +-69 +-53 +-2 +35 +61 +67 +-6 +-44 +-69 +-53 +-1 +36 +62 +69 +-4 +-42 +-68 +-51 +1 +37 +64 +70 +-3 +-41 +-67 +-50 +2 +38 +64 +70 +-3 +-42 +-67 +-51 +1 +38 +64 +69 +-3 +-42 +-67 +-83 +-60 +-5 +36 +63 +83 +84 +7 +-35 +-63 +-81 +-59 +-4 +36 +63 +82 +83 +7 +-36 +-63 +-81 +-59 +-4 +35 +63 +82 +83 +7 +-36 +-63 +-81 +-59 +-4 +36 +63 +82 +83 +7 +-36 +-64 +-82 +-59 +-5 +35 +62 +81 +83 +6 +-36 +-64 +-49 +2 +37 +62 +68 +-5 +-43 +-68 +-52 +0 +36 +62 +68 +-4 +-43 +-68 +-51 +1 +37 +63 +69 +-4 +-42 +-67 +-51 +1 +37 +64 +70 +-3 +-42 +-67 +-51 +1 +37 +63 +69 +-3 +-42 +-67 +-51 +2 +38 +64 +70 +-3 +-41 +-66 +-50 +2 +38 +64 +80 +80 +3 +-39 +-67 +-85 +-63 +-8 +32 +59 +78 +80 +3 +-39 +-67 +-84 +-62 +-7 +33 +61 +79 +82 +4 +-37 +-65 +-83 +-61 +-6 +33 +62 +79 +82 +5 +-37 +-65 +-83 +-61 +-5 +34 +62 +80 +82 +5 +-37 +-65 +-82 +-60 +-5 +33 +62 +70 +-1 +-40 +-65 +-48 +3 +40 +66 +72 +-1 +-40 +-65 +-49 +3 +39 +65 +71 +-1 +-41 +-65 +-50 +2 +39 +64 +71 +-2 +-41 +-66 +-50 +2 +39 +64 +71 +-2 +-41 +-66 +-50 +2 +39 +64 +71 +-2 +-41 +-65 +-49 +2 +39 +65 +81 +82 +4 +-38 +-66 +-84 +-62 +-7 +32 +61 +79 +81 +5 +-38 +-65 +-83 +-61 +-6 +34 +61 +80 +82 +5 +-37 +-65 +-82 +-61 +-6 +34 +62 +80 +82 +5 +-37 +-64 +-82 +-60 +-5 +34 +63 +80 +83 +6 +-37 +-64 +-82 +-60 +-5 +34 +62 +81 +82 +5 +-37 +-65 +-82 +-61 +-5 +34 +62 +81 +82 +5 +-37 +-64 +-82 +-60 +-5 +34 +62 +80 +82 +6 +-37 +-64 +-82 +-61 +-5 +34 +62 +81 +82 +5 +-37 +-65 +-82 +-60 +-5 +34 +62 +81 +83 +6 +-36 +-64 +-81 +-60 +-5 +34 +62 +70 +-1 +-39 +-64 +-48 +4 +41 +66 +72 +0 +-39 +-64 +-48 +3 +40 +66 +72 +-1 +-41 +-66 +-50 +2 +39 +64 +71 +-2 +-41 +-66 +-50 +2 +39 +64 +71 +-2 +-41 +-66 +-50 +2 +38 +64 +70 +-3 +-42 +-67 +-51 +2 +38 +64 +70 +-3 +-42 +-67 +-51 +1 +38 +64 +70 +-3 +-42 +-67 +-51 +1 +38 +64 +70 +-2 +-42 +-66 +-50 +2 +39 +64 +70 +-2 +-41 +-66 +-50 +2 +39 +64 +71 +-2 +-41 +-66 +-50 +2 +39 +65 +71 +-2 +-41 +-66 +-50 +2 +38 +64 +70 +-3 +-41 +-66 +-82 +-59 +-4 +37 +65 +83 +85 +8 +-34 +-62 +-80 +-58 +-3 +36 +64 +82 +84 +7 +-36 +-63 +-81 +-60 +-5 +35 +62 +81 +82 +6 +-37 +-65 +-82 +-60 +-5 +35 +62 +81 +82 +5 +-37 +-64 +-82 +-60 +-5 +35 +62 +81 +82 +6 +-37 +-64 +-50 +1 +37 +62 +68 +-5 +-44 +-68 +-52 +0 +36 +62 +68 +-4 +-43 +-68 +-52 +0 +37 +62 +69 +-3 +-43 +-67 +-51 +1 +38 +63 +69 +-3 +-42 +-67 +-51 +1 +38 +63 +69 +-3 +-43 +-67 +-51 +1 +38 +63 +69 +-3 +-43 +-67 +-51 +0 +37 +63 +79 +80 +2 +-41 +-68 +-86 +-64 +-8 +31 +59 +78 +79 +3 +-39 +-66 +-84 +-62 +-7 +33 +61 +79 +81 +4 +-38 +-65 +-83 +-61 +-6 +34 +61 +80 +82 +5 +-37 +-65 +-83 +-61 +-6 +34 +62 +80 +82 +5 +-37 +-65 +-82 +-61 +-5 +34 +62 +70 +-2 +-40 +-64 +-48 +4 +40 +66 +71 +-1 +-40 +-65 +-49 +3 +39 +65 +70 +-2 +-41 +-67 +-50 +2 +38 +64 +70 +-3 +-42 +-67 +-50 +2 +38 +64 +70 +-2 +-41 +-66 +-50 +2 +38 +64 +70 +-2 +-41 +-66 +-50 +2 +39 +65 +81 +81 +4 +-39 +-66 +-84 +-62 +-7 +32 +61 +79 +81 +4 +-38 +-65 +-83 +-62 +-6 +33 +61 +80 +82 +5 +-38 +-66 +-83 +-61 +-6 +34 +62 +80 +82 +5 +-37 +-65 +-83 +-61 +-6 +34 +61 +80 +82 +5 +-37 +-65 +-82 +-61 +-6 +34 +62 +70 +-1 +-40 +-64 +-48 +4 +41 +66 +72 +0 +-39 +-64 +-48 +3 +40 +66 +71 +-1 +-40 +-65 +-49 +3 +39 +65 +71 +-2 +-40 +-66 +-49 +2 +39 +65 +71 +-2 +-41 +-66 +-49 +2 +39 +65 +71 +-2 +-41 +-66 +-49 +2 +39 +65 +71 +-2 +-41 +-66 +-82 +-59 +-3 +37 +65 +84 +85 +9 +-34 +-62 +-80 +-58 +-3 +36 +64 +82 +83 +7 +-36 +-63 +-81 +-59 +-5 +35 +63 +81 +83 +6 +-37 +-64 +-82 +-60 +-5 +35 +63 +81 +82 +6 +-37 +-64 +-81 +-60 +-5 +35 +62 +81 +82 +6 +-37 +-64 +-50 +1 +37 +62 +68 +-5 +-44 +-69 +-53 +-1 +36 +62 +68 +-4 +-43 +-68 +-51 +1 +37 +63 +69 +-3 +-42 +-67 +-51 +1 +37 +63 +69 +-3 +-42 +-67 +-51 +1 +38 +64 +70 +-3 +-41 +-66 +-50 +2 +38 +64 +70 +-2 +-41 +-66 +-83 +-59 +-4 +36 +64 +83 +84 +8 +-35 +-62 +-80 +-58 +-4 +36 +63 +82 +83 +7 +-36 +-64 +-82 +-60 +-5 +35 +62 +81 +82 +6 +-37 +-64 +-82 +-60 +-6 +34 +61 +80 +81 +4 +-38 +-65 +-84 +-61 +-6 +34 +61 +80 +81 +5 +-38 +-65 +-51 +0 +36 +61 +67 +-5 +-45 +-69 +-53 +-1 +36 +61 +68 +-5 +-44 +-68 +-51 +0 +37 +63 +69 +-4 +-43 +-67 +-51 +1 +38 +63 +70 +-3 +-42 +-67 +-51 +1 +38 +64 +70 +-3 +-41 +-66 +-50 +2 +39 +64 +70 +-3 +-42 +-66 +-50 +2 +38 +64 +80 +80 +3 +-39 +-67 +-85 +-64 +-8 +31 +59 +78 +80 +3 +-39 +-67 +-84 +-62 +-7 +33 +60 +79 +81 +4 +-38 +-66 +-84 +-62 +-7 +33 +61 +79 +81 +4 +-38 +-66 +-83 +-62 +-6 +33 +61 +80 +82 +5 +-37 +-65 +-82 +-61 +-5 +34 +62 +70 +-1 +-39 +-64 +-48 +4 +41 +67 +73 +1 +-38 +-63 +-47 +4 +41 +66 +72 +-1 +-39 +-64 +-48 +4 +40 +66 +72 +-1 +-40 +-65 +-49 +3 +39 +65 +71 +-2 +-41 +-66 +-50 +2 +38 +64 +71 +-2 +-41 +-67 +-51 +1 +37 +64 +80 +80 +3 +-39 +-68 +-85 +-64 +-8 +31 +59 +78 +79 +2 +-39 +-67 +-85 +-63 +-8 +32 +60 +79 +81 +3 +-38 +-66 +-84 +-62 +-7 +32 +61 +79 +81 +5 +-38 +-66 +-83 +-62 +-6 +33 +61 +79 +81 +5 +-37 +-66 +-83 +-61 +-6 +34 +61 +70 +-1 +-40 +-65 +-49 +3 +40 +65 +72 +-1 +-41 +-65 +-49 +3 +39 +65 +71 +-2 +-41 +-66 +-50 +2 +39 +65 +71 +-2 +-41 +-66 +-50 +2 +39 +65 +71 +-2 +-41 +-66 +-50 +2 +39 +65 +71 +-2 +-41 +-66 +-50 +3 +39 +65 +71 +-2 +-41 +-66 +-82 +-58 +-3 +37 +65 +84 +85 +9 +-34 +-61 +-79 +-57 +-2 +37 +65 +83 +84 +8 +-35 +-62 +-80 +-58 +-3 +36 +64 +83 +84 +8 +-35 +-63 +-81 +-59 +-4 +36 +63 +82 +83 +7 +-36 +-63 +-81 +-59 +-4 +36 +63 +82 +83 +6 +-36 +-64 +-50 +1 +37 +62 +68 +-5 +-44 +-69 +-53 +-1 +35 +61 +68 +-5 +-44 +-69 +-52 +0 +37 +63 +69 +-4 +-43 +-68 +-52 +0 +37 +63 +69 +-4 +-43 +-67 +-51 +1 +37 +63 +69 +-4 +-42 +-67 +-51 +1 +38 +63 +70 +-3 +-42 +-67 +-83 +-59 +-4 +36 +64 +83 +84 +8 +-35 +-62 +-80 +-58 +-3 +36 +64 +82 +83 +7 +-36 +-63 +-81 +-59 +-4 +35 +62 +82 +83 +6 +-36 +-64 +-82 +-60 +-5 +35 +62 +81 +82 +6 +-37 +-64 +-82 +-60 +-5 +35 +62 +81 +83 +6 +-36 +-63 +-49 +2 +37 +63 +69 +-4 +-43 +-68 +-51 +0 +37 +63 +69 +-4 +-43 +-67 +-51 +1 +37 +63 +69 +-4 +-43 +-68 +-51 +1 +37 +63 +69 +-3 +-42 +-68 +-51 +1 +37 +63 +69 +-3 +-42 +-67 +-51 +1 +37 +63 +69 +-4 +-42 +-67 +-51 +1 +38 +64 +80 +80 +3 +-40 +-68 +-85 +-63 +-8 +32 +60 +78 +80 +3 +-38 +-66 +-83 +-61 +-6 +33 +61 +79 +81 +4 +-38 +-65 +-83 +-61 +-6 +33 +62 +80 +82 +5 +-37 +-64 +-82 +-60 +-5 +34 +62 +81 +82 +6 +-36 +-65 +-82 +-60 +-5 +34 +62 +71 +0 +-39 +-63 +-47 +5 +41 +67 +73 +0 +-39 +-64 +-48 +4 +40 +66 +72 +-1 +-40 +-65 +-49 +3 +39 +65 +71 +-1 +-40 +-65 +-49 +3 +40 +65 +71 +-2 +-41 +-65 +-49 +3 +39 +65 +71 +-2 +-41 +-66 +-50 +2 +39 +64 +81 +81 +4 +-39 +-67 +-84 +-62 +-7 +32 +60 +78 +80 +3 +-39 +-66 +-84 +-62 +-6 +32 +61 +79 +81 +5 +-38 +-65 +-83 +-61 +-5 +34 +62 +81 +82 +6 +-36 +-64 +-81 +-60 +-5 +35 +62 +81 +83 +6 +-36 +-64 +-81 +-59 +-5 +35 +63 +70 +0 +-39 +-64 +-47 +4 +41 +66 +72 +0 +-39 +-64 +-48 +3 +40 +65 +71 +-1 +-40 +-65 +-49 +3 +39 +65 +71 +-2 +-41 +-66 +-50 +2 +39 +65 +71 +-2 +-41 +-66 +-50 +2 +39 +64 +70 +-2 +-42 +-66 +-50 +2 +38 +64 +70 +-3 +-42 +-67 +-83 +-59 +-4 +36 +64 +83 +84 +8 +-35 +-63 +-81 +-58 +-4 +35 +63 +82 +83 +7 +-36 +-64 +-82 +-60 +-5 +35 +62 +81 +82 +6 +-37 +-64 +-82 +-60 +-6 +34 +62 +80 +81 +5 +-38 +-65 +-83 +-61 +-6 +34 +62 +80 +82 +6 +-37 +-64 +-50 +1 +37 +62 +68 +-5 +-44 +-69 +-53 +-1 +36 +62 +67 +-5 +-44 +-69 +-52 +0 +37 +63 +69 +-3 +-43 +-67 +-51 +1 +38 +64 +70 +-3 +-42 +-66 +-50 +1 +39 +64 +70 +-2 +-42 +-66 +-50 +2 +39 +65 +70 +-2 +-41 +-66 +-82 +-59 +-3 +37 +65 +84 +85 +8 +-34 +-62 +-80 +-58 +-4 +36 +63 +82 +83 +6 +-36 +-64 +-82 +-60 +-5 +35 +63 +81 +82 +7 +-37 +-64 +-81 +-60 +-5 +35 +62 +81 +82 +6 +-36 +-64 +-82 +-60 +-5 +35 +62 +82 +83 +6 +-36 +-64 +-49 +2 +38 +63 +69 +-4 +-43 +-68 +-52 +0 +37 +63 +69 +-4 +-43 +-68 +-52 +1 +37 +63 +69 +-4 +-42 +-67 +-51 +1 +38 +64 +69 +-3 +-42 +-67 +-51 +1 +37 +64 +69 +-4 +-42 +-67 +-51 +1 +37 +63 +69 +-3 +-42 +-67 +-51 +1 +37 +64 +80 +80 +3 +-40 +-67 +-85 +-63 +-8 +32 +60 +78 +80 +3 +-39 +-66 +-83 +-62 +-6 +33 +61 +79 +82 +5 +-37 +-65 +-83 +-61 +-6 +33 +62 +80 +82 +5 +-37 +-65 +-83 +-61 +-6 +33 +61 +80 +81 +5 +-38 +-65 +-83 +-62 +-6 +33 +61 +80 +82 +5 +-37 +-65 +-83 +-61 +-5 +34 +62 +81 +82 +6 +-37 +-65 +-82 +-60 +-5 +34 +62 +80 +82 +5 +-37 +-65 +-82 +-60 +-5 +35 +63 +81 +83 +6 +-36 +-64 +-81 +-59 +-4 +35 +63 +81 +83 +7 +-36 +-63 +-81 +-59 +-4 +35 +63 +71 +-1 +-39 +-64 +-47 +5 +41 +67 +73 +0 +-39 +-64 +-49 +3 +40 +65 +71 +-2 +-40 +-65 +-49 +3 +39 +65 +71 +-2 +-41 +-65 +-50 +2 +39 +65 +71 +-2 +-41 +-66 +-50 +2 +39 +64 +70 +-2 +-41 +-66 +-50 +2 +39 +64 +81 +81 +3 +-39 +-67 +-85 +-64 +-8 +31 +59 +78 +80 +3 +-39 +-67 +-85 +-63 +-8 +32 +60 +78 +80 +3 +-39 +-66 +-84 +-62 +-7 +32 +61 +79 +81 +4 +-38 +-66 +-83 +-61 +-6 +33 +62 +80 +82 +5 +-37 +-65 +-82 +-60 +-5 +35 +63 +71 +0 +-39 +-63 +-47 +5 +41 +67 +73 +1 +-39 +-63 +-47 +4 +41 +66 +72 +0 +-40 +-65 +-49 +3 +40 +66 +72 +-1 +-40 +-65 +-50 +2 +39 +64 +71 +-2 +-41 +-66 +-50 +2 +38 +64 +70 +-2 +-41 +-67 +-50 +2 +38 +64 +70 +-2 +-41 +-66 +-50 +2 +39 +65 +70 +-2 +-41 +-67 +-50 +1 +38 +64 +70 +-3 +-41 +-67 +-50 +1 +38 +64 +69 +-3 +-43 +-67 +-51 +0 +37 +63 +69 +-4 +-43 +-68 +-52 +0 +37 +62 +68 +-4 +-43 +-68 +-52 +0 +37 +63 +69 +-3 +-43 +-67 +-83 +-60 +-4 +36 +64 +83 +84 +9 +-34 +-62 +-79 +-58 +-3 +37 +64 +83 +84 +8 +-35 +-63 +-81 +-58 +-4 +36 +63 +82 +84 +7 +-35 +-63 +-81 +-59 +-4 +36 +63 +82 +83 +7 +-36 +-63 +-81 +-59 +-4 +35 +63 +82 +83 +7 +-36 +-63 +-81 +-60 +-5 +35 +62 +82 +83 +6 +-36 +-64 +-82 +-60 +-5 +35 +62 +81 +82 +6 +-37 +-65 +-83 +-60 +-6 +34 +61 +80 +82 +5 +-38 +-65 +-83 +-61 +-6 +33 +61 +80 +81 +5 +-38 +-65 +-83 +-61 +-6 +34 +61 +80 +81 +5 +-38 +-66 +-51 +0 +35 +61 +67 +-6 +-45 +-69 +-53 +-1 +36 +62 +68 +-4 +-43 +-68 +-52 +1 +37 +63 +70 +-3 +-42 +-67 +-50 +2 +39 +65 +70 +-2 +-41 +-66 +-49 +2 +39 +65 +71 +-1 +-41 +-65 +-49 +2 +39 +65 +71 +-1 +-41 +-66 +-49 +2 +39 +65 +81 +82 +4 +-38 +-66 +-84 +-62 +-7 +32 +60 +78 +80 +3 +-39 +-67 +-84 +-62 +-7 +32 +61 +79 +81 +4 +-39 +-66 +-84 +-62 +-6 +33 +61 +79 +81 +4 +-38 +-66 +-84 +-62 +-6 +33 +61 +80 +82 +5 +-37 +-65 +-83 +-61 +-6 +33 +62 +70 +-2 +-40 +-65 +-48 +4 +40 +66 +72 +0 +-39 +-65 +-49 +3 +39 +65 +70 +-2 +-41 +-66 +-49 +2 +39 +65 +71 +-2 +-41 +-66 +-49 +2 +39 +65 +70 +-2 +-41 +-66 +-49 +2 +39 +65 +71 +-2 +-41 +-66 +-49 +2 +39 +65 +71 +-1 +-41 +-65 +-49 +2 +39 +65 +71 +-2 +-41 +-66 +-50 +2 +39 +65 +71 +-2 +-41 +-66 +-49 +2 +39 +64 +70 +-3 +-42 +-67 +-51 +1 +38 +64 +69 +-3 +-42 +-67 +-51 +1 +38 +63 +69 +-3 +-43 +-68 +-51 +0 +37 +63 +69 +-3 +-42 +-67 +-83 +-60 +-4 +36 +63 +83 +84 +8 +-35 +-62 +-80 +-58 +-4 +36 +63 +82 +83 +7 +-36 +-63 +-81 +-59 +-5 +35 +63 +82 +83 +6 +-36 +-63 +-82 +-59 +-5 +35 +63 +81 +83 +7 +-36 +-63 +-81 +-59 +-5 +35 +62 +81 +82 +6 +-37 +-64 +-81 +-59 +-5 +35 +62 +82 +83 +6 +-36 +-64 +-82 +-60 +-5 +34 +62 +81 +82 +6 +-37 +-64 +-83 +-60 +-5 +34 +62 +80 +82 +6 +-37 +-64 +-82 +-60 +-5 +35 +62 +81 +82 +7 +-37 +-63 +-81 +-60 +-5 +35 +62 +81 +82 +6 +-36 +-64 +-49 +1 +37 +62 +67 +-5 +-44 +-69 +-53 +-1 +36 +62 +67 +-5 +-44 +-69 +-52 +0 +36 +62 +68 +-4 +-43 +-68 +-52 +0 +37 +63 +69 +-4 +-42 +-68 +-51 +1 +37 +63 +69 +-3 +-42 +-66 +-50 +2 +38 +64 +70 +-3 +-42 +-67 +-51 +1 +38 +64 +70 +-3 +-42 +-67 +-51 +1 +37 +63 +69 +-4 +-43 +-68 +-51 +1 +37 +63 +69 +-4 +-42 +-67 +-51 +1 +37 +63 +69 +-3 +-42 +-67 +-51 +1 +37 +64 +70 +-3 +-41 +-67 +-50 +2 +38 +64 +70 +-2 +-41 +-66 +-50 +2 +39 +65 +81 +82 +4 +-39 +-66 +-84 +-62 +-7 +32 +61 +79 +81 +4 +-38 +-66 +-83 +-62 +-6 +33 +61 +80 +81 +5 +-38 +-66 +-83 +-62 +-6 +33 +61 +79 +81 +4 +-38 +-66 +-83 +-62 +-7 +33 +61 +79 +81 +4 +-38 +-66 +-83 +-61 +-6 +33 +61 +80 +81 +5 +-37 +-65 +-83 +-61 +-6 +33 +61 +80 +82 +5 +-37 +-65 +-82 +-61 +-5 +34 +62 +81 +83 +6 +-36 +-64 +-81 +-60 +-5 +35 +63 +81 +83 +6 +-36 +-64 +-82 +-60 +-5 +35 +63 +81 +83 +6 +-37 +-64 +-82 +-60 +-5 +34 +62 +70 +-1 +-39 +-64 +-48 +4 +41 +66 +72 +-1 +-40 +-65 +-49 +3 +40 +65 +71 +-2 +-40 +-65 +-49 +3 +39 +65 +71 +-2 +-41 +-66 +-49 +3 +39 +65 +71 +-2 +-41 +-66 +-49 +3 +39 +65 +71 +-2 +-40 +-65 +-49 +3 +39 +65 +71 +-2 +-40 +-66 +-49 +3 +39 +66 +71 +-2 +-42 +-67 +-50 +2 +39 +66 +72 +-1 +-41 +-68 +-52 +1 +38 +65 +71 +-1 +-41 +-67 +-52 +-1 +37 +64 +71 +-1 +-41 +-66 +-50 +1 +37 +63 +69 +-3 +-42 +-67 +-51 +1 +38 +64 +69 +-4 +-43 +-68 +-84 +-60 +-4 +37 +65 +83 +83 +6 +-36 +-64 +-81 +-59 +-4 +36 +63 +82 +83 +6 +-36 +-63 +-82 +-61 +-6 +35 +64 +83 +84 +7 +-36 +-64 +-83 +-61 +-5 +35 +64 +83 +83 +6 +-38 +-65 +-83 +-60 +-5 +36 +63 +82 +82 +5 +-37 +-64 +-50 +2 +37 +63 +69 +-4 +-43 +-68 +-52 +0 +37 +63 +69 +-4 +-42 +-67 +-51 +1 +38 +64 +70 +-3 +-42 +-66 +-50 +2 +38 +65 +71 +-2 +-41 +-66 +-50 +2 +39 +65 +71 +-2 +-41 +-66 +-50 +2 +39 +65 +71 +-2 +-41 +-66 +-82 +-58 +-3 +37 +65 +83 +85 +8 +-35 +-62 +-81 +-58 +-4 +36 +63 +82 +83 +7 +-36 +-63 +-81 +-59 +-5 +35 +62 +81 +82 +6 +-37 +-64 +-82 +-60 +-5 +35 +62 +81 +82 +6 +-37 +-64 +-82 +-60 +-6 +34 +61 +81 +82 +5 +-37 +-64 +-83 +-60 +-6 +34 +61 +80 +82 +5 +-37 +-64 +-83 +-60 +-5 +34 +62 +80 +82 +6 +-37 +-64 +-82 +-60 +-5 +34 +62 +81 +82 +6 +-37 +-64 +-82 +-60 +-5 +35 +62 +81 +83 +6 +-36 +-64 +-82 +-59 +-5 +35 +62 +81 +83 +6 +-37 +-64 +-50 +1 +37 +62 +68 +-5 +-44 +-69 +-53 +-1 +36 +62 +68 +-5 +-43 +-69 +-52 +0 +36 +62 +69 +-4 +-43 +-68 +-52 +0 +36 +63 +69 +-4 +-43 +-68 +-52 +0 +37 +63 +69 +-4 +-43 +-68 +-51 +0 +37 +63 +69 +-4 +-43 +-68 +-51 +1 +37 +63 +79 +80 +3 +-40 +-68 +-85 +-64 +-8 +31 +59 +78 +80 +3 +-39 +-67 +-84 +-62 +-7 +33 +61 +79 +82 +5 +-38 +-66 +-83 +-61 +-6 +33 +62 +80 +82 +5 +-37 +-65 +-83 +-61 +-5 +34 +62 +81 +82 +6 +-37 +-64 +-82 +-60 +-5 +35 +63 +71 +0 +-39 +-63 +-47 +5 +42 +67 +73 +0 +-39 +-64 +-48 +4 +41 +67 +73 +0 +-39 +-64 +-48 +4 +40 +66 +72 +-1 +-40 +-65 +-49 +3 +39 +65 +71 +-2 +-41 +-66 +-50 +2 +38 +64 +70 +-3 +-42 +-67 +-51 +1 +38 +64 +70 +-3 +-42 +-67 +-50 +2 +38 +64 +69 +-3 +-42 +-67 +-51 +1 +37 +63 +69 +-3 +-43 +-68 +-51 +0 +37 +63 +69 +-3 +-42 +-68 +-51 +1 +38 +64 +70 +-2 +-41 +-66 +-49 +3 +40 +65 +71 +-1 +-40 +-65 +-49 +3 +40 +65 +71 +-1 +-41 +-66 +-82 +-59 +-3 +37 +65 +84 +85 +9 +-34 +-62 +-80 +-58 +-3 +36 +63 +82 +83 +7 +-35 +-63 +-81 +-59 +-5 +35 +62 +81 +83 +6 +-36 +-64 +-83 +-61 +-5 +34 +62 +80 +81 +5 +-38 +-65 +-83 +-61 +-6 +34 +62 +81 +81 +6 +-37 +-65 +-50 +0 +37 +62 +67 +-5 +-44 +-69 +-52 +0 +37 +64 +70 +-3 +-43 +-69 +-52 +0 +38 +65 +71 +-1 +-41 +-68 +-52 +-1 +37 +64 +71 +-1 +-40 +-66 +-50 +0 +37 +63 +70 +-2 +-42 +-67 +-51 +1 +38 +63 +69 +-3 +-42 +-67 +-83 +-60 +-4 +37 +65 +83 +84 +7 +-36 +-63 +-81 +-59 +-4 +36 +63 +82 +83 +7 +-36 +-63 +-82 +-61 +-6 +35 +63 +83 +84 +7 +-36 +-65 +-83 +-61 +-5 +36 +65 +83 +84 +6 +-38 +-65 +-83 +-60 +-4 +36 +64 +82 +82 +5 +-37 +-64 +-50 +1 +37 +62 +68 +-4 +-43 +-68 +-52 +-1 +37 +62 +68 +-4 +-43 +-68 +-52 +0 +37 +63 +69 +-3 +-43 +-67 +-51 +1 +37 +63 +69 +-4 +-43 +-67 +-51 +1 +37 +63 +69 +-4 +-43 +-67 +-51 +0 +37 +63 +69 +-3 +-43 +-68 +-51 +0 +37 +63 +79 +80 +2 +-40 +-68 +-86 +-64 +-8 +31 +60 +78 +80 +3 +-38 +-66 +-84 +-61 +-6 +33 +62 +80 +82 +5 +-37 +-64 +-82 +-61 +-5 +34 +62 +81 +82 +5 +-37 +-64 +-82 +-60 +-5 +35 +62 +81 +83 +6 +-36 +-64 +-81 +-59 +-5 +35 +63 +81 +83 +6 +-36 +-64 +-82 +-60 +-5 +34 +63 +81 +83 +6 +-37 +-64 +-82 +-60 +-5 +34 +62 +81 +82 +6 +-37 +-64 +-82 +-61 +-5 +34 +62 +80 +82 +5 +-37 +-65 +-82 +-61 +-5 +34 +62 +80 +82 +5 +-37 +-65 +-82 +-60 +-5 +34 +62 +70 +-1 +-40 +-64 +-48 +4 +41 +66 +72 +-1 +-40 +-65 +-49 +3 +39 +65 +71 +-1 +-41 +-66 +-49 +2 +39 +64 +70 +-3 +-42 +-67 +-50 +1 +38 +64 +70 +-2 +-42 +-67 +-50 +1 +38 +64 +70 +-3 +-42 +-67 +-50 +1 +38 +64 +70 +-2 +-42 +-67 +-50 +1 +38 +64 +70 +-3 +-42 +-67 +-50 +1 +38 +64 +70 +-2 +-42 +-66 +-50 +2 +38 +64 +70 +-2 +-42 +-66 +-50 +2 +39 +64 +70 +-3 +-42 +-67 +-50 +1 +38 +63 +69 +-3 +-42 +-67 +-51 +1 +38 +63 +69 +-3 +-42 +-67 +-84 +-60 +-5 +35 +63 +82 +84 +7 +-36 +-63 +-81 +-59 +-4 +36 +63 +82 +83 +7 +-36 +-64 +-82 +-59 +-5 +35 +62 +82 +83 +6 +-36 +-64 +-82 +-60 +-5 +35 +63 +81 +83 +6 +-36 +-63 +-81 +-59 +-4 +35 +63 +82 +83 +7 +-36 +-63 +-49 +2 +38 +63 +69 +-4 +-43 +-68 +-51 +0 +37 +63 +69 +-3 +-43 +-67 +-51 +1 +38 +64 +70 +-3 +-42 +-67 +-51 +1 +38 +63 +70 +-3 +-42 +-67 +-51 +1 +38 +64 +70 +-3 +-42 +-66 +-50 +2 +39 +64 +71 +-2 +-42 +-66 +-82 +-59 +-3 +37 +65 +84 +85 +9 +-34 +-62 +-79 +-58 +-3 +37 +64 +83 +83 +7 +-36 +-64 +-82 +-59 +-5 +35 +63 +82 +83 +6 +-37 +-64 +-82 +-60 +-5 +35 +62 +81 +83 +6 +-37 +-64 +-82 +-60 +-5 +34 +62 +80 +82 +6 +-37 +-65 +-51 +0 +36 +61 +67 +-6 +-45 +-70 +-54 +-2 +35 +61 +67 +-5 +-44 +-69 +-53 +-1 +36 +62 +68 +-5 +-44 +-68 +-52 +0 +37 +62 +69 +-4 +-43 +-67 +-51 +1 +38 +63 +70 +-3 +-42 +-67 +-51 +1 +38 +64 +70 +-2 +-41 +-66 +-50 +2 +39 +64 +81 +81 +4 +-38 +-67 +-84 +-62 +-7 +33 +61 +80 +82 +5 +-37 +-65 +-82 +-60 +-5 +35 +63 +81 +83 +6 +-36 +-64 +-82 +-60 +-4 +35 +63 +81 +83 +7 +-36 +-63 +-82 +-60 +-4 +35 +63 +81 +83 +6 +-37 +-64 +-81 +-60 +-5 +34 +62 +71 +-1 +-39 +-64 +-48 +4 +41 +66 +72 +-1 +-40 +-65 +-49 +3 +39 +65 +71 +-2 +-41 +-66 +-51 +1 +38 +64 +70 +-3 +-42 +-67 +-51 +1 +37 +63 +69 +-4 +-42 +-67 +-51 +1 +38 +64 +70 +-3 +-42 +-67 +-51 +1 +38 +64 +80 +80 +3 +-40 +-68 +-85 +-64 +-8 +32 +59 +78 +80 +3 +-39 +-67 +-84 +-62 +-7 +32 +60 +79 +81 +4 +-38 +-66 +-83 +-61 +-6 +33 +62 +80 +82 +5 +-37 +-65 +-82 +-60 +-5 +34 +62 +81 +83 +6 +-37 +-64 +-82 +-60 +-5 +35 +62 +71 +0 +-39 +-63 +-47 +5 +41 +67 +72 +0 +-39 +-64 +-49 +3 +40 +66 +72 +-1 +-40 +-65 +-49 +2 +39 +65 +71 +-2 +-41 +-66 +-50 +2 +38 +64 +70 +-3 +-42 +-67 +-51 +1 +38 +63 +69 +-3 +-43 +-68 +-51 +1 +38 +63 +69 +-3 +-42 +-67 +-51 +1 +38 +63 +70 +-3 +-42 +-67 +-51 +1 +38 +64 +70 +-3 +-42 +-67 +-51 +2 +38 +64 +70 +-3 +-42 +-66 +-50 +2 +38 +64 +71 +-2 +-41 +-66 +-50 +2 +39 +65 +71 +-2 +-41 +-66 +-49 +3 +39 +65 +71 +-1 +-40 +-66 +-49 +3 +39 +65 +71 +-2 +-41 +-66 +-50 +2 +38 +64 +70 +-3 +-42 +-67 +-50 +2 +38 +64 +70 +-3 +-42 +-67 +-51 +2 +38 +64 +70 +-3 +-42 +-67 +-50 +2 +38 +64 +70 +-3 +-42 +-67 +-51 +1 +37 +64 +70 +-3 +-42 +-67 +-51 +1 +38 +64 +80 +80 +3 +-40 +-68 +-85 +-63 +-8 +32 +59 +78 +80 +3 +-39 +-67 +-84 +-62 +-7 +33 +61 +79 +81 +5 +-37 +-65 +-83 +-61 +-6 +34 +62 +80 +82 +5 +-37 +-65 +-83 +-61 +-5 +33 +62 +80 +82 +5 +-37 +-65 +-83 +-61 +-6 +33 +61 +80 +82 +5 +-38 +-65 +-83 +-61 +-6 +33 +62 +80 +82 +5 +-37 +-65 +-83 +-61 +-5 +34 +62 +80 +81 +5 +-38 +-66 +-83 +-61 +-6 +34 +61 +80 +82 +5 +-37 +-65 +-82 +-60 +-5 +34 +62 +80 +82 +5 +-37 +-65 +-83 +-61 +-5 +34 +62 +80 +82 +5 +-37 +-65 +-82 +-61 +-5 +34 +62 +81 +82 +5 +-37 +-65 +-82 +-61 +-5 +34 +61 +80 +82 +5 +-37 +-65 +-82 +-60 +-5 +34 +62 +81 +83 +6 +-37 +-64 +-82 +-60 +-4 +35 +63 +81 +83 +6 +-36 +-64 +-82 +-59 +-4 +35 +63 +71 +-1 +-39 +-64 +-47 +5 +42 +67 +73 +1 +-39 +-64 +-48 +4 +40 +66 +72 +-1 +-39 +-65 +-48 +3 +40 +66 +71 +-1 +-40 +-65 +-49 +3 +40 +66 +71 +-1 +-40 +-66 +-49 +3 +39 +65 +71 +-2 +-41 +-66 +-49 +2 +39 +65 +81 +82 +4 +-39 +-67 +-84 +-62 +-7 +32 +61 +79 +81 +4 +-38 +-66 +-83 +-62 +-6 +33 +62 +80 +82 +5 +-37 +-65 +-82 +-61 +-6 +33 +61 +80 +81 +5 +-38 +-66 +-83 +-62 +-6 +33 +61 +79 +81 +4 +-38 +-66 +-84 +-62 +-7 +33 +61 +79 +81 +4 +-38 +-65 +-83 +-62 +-6 +33 +61 +80 +82 +5 +-38 +-66 +-83 +-61 +-6 +34 +62 +80 +82 +5 +-37 +-65 +-83 +-61 +-6 +34 +62 +80 +82 +5 +-37 +-64 +-82 +-60 +-5 +34 +62 +80 +82 +6 +-37 +-64 +-82 +-60 +-4 +34 +63 +71 +-1 +-39 +-63 +-47 +5 +41 +67 +73 +1 +-38 +-64 +-47 +5 +41 +67 +72 +0 +-39 +-65 +-48 +3 +40 +66 +71 +-2 +-41 +-66 +-50 +2 +39 +65 +70 +-2 +-41 +-67 +-50 +2 +38 +64 +70 +-3 +-42 +-67 +-50 +2 +38 +64 +70 +-3 +-42 +-67 +-51 +2 +38 +64 +70 +-3 +-41 +-66 +-50 +2 +38 +64 +70 +-3 +-41 +-67 +-50 +2 +38 +65 +71 +-2 +-41 +-66 +-49 +3 +39 +65 +71 +-2 +-41 +-66 +-50 +3 +39 +65 +71 +-2 +-41 +-66 +-49 +2 +39 +65 +71 +-2 +-41 +-66 +-82 +-59 +-3 +38 +65 +84 +85 +9 +-34 +-61 +-80 +-57 +-3 +37 +64 +83 +84 +7 +-35 +-63 +-81 +-59 +-5 +35 +63 +81 +83 +6 +-37 +-64 +-83 +-60 +-5 +34 +62 +81 +82 +6 +-37 +-65 +-83 +-61 +-6 +34 +61 +80 +81 +5 +-38 +-66 +-51 +0 +35 +61 +66 +-6 +-45 +-71 +-54 +-2 +35 +61 +66 +-6 +-45 +-70 +-53 +-1 +35 +62 +67 +-5 +-44 +-69 +-52 +-1 +36 +62 +68 +-4 +-43 +-68 +-52 +1 +37 +63 +69 +-3 +-42 +-67 +-50 +2 +39 +65 +71 +-2 +-41 +-66 +-50 +2 +39 +65 +81 +82 +4 +-39 +-66 +-84 +-62 +-7 +33 +61 +79 +81 +4 +-38 +-66 +-83 +-61 +-6 +34 +61 +80 +82 +5 +-37 +-65 +-82 +-61 +-5 +34 +62 +81 +83 +6 +-36 +-64 +-82 +-60 +-5 +34 +62 +81 +82 +6 +-37 +-64 +-81 +-60 +-5 +34 +62 +70 +-1 +-39 +-64 +-48 +4 +40 +66 +72 +-1 +-40 +-65 +-49 +3 +39 +65 +71 +-2 +-41 +-66 +-50 +2 +38 +64 +70 +-3 +-42 +-67 +-50 +2 +38 +64 +70 +-3 +-42 +-67 +-50 +2 +38 +64 +70 +-3 +-41 +-66 +-50 +2 +38 +64 +81 +81 +4 +-38 +-67 +-84 +-62 +-7 +33 +60 +79 +81 +3 +-38 +-66 +-84 +-62 +-7 +33 +61 +79 +81 +4 +-38 +-66 +-84 +-62 +-6 +33 +61 +79 +81 +5 +-38 +-65 +-83 +-61 +-5 +33 +62 +80 +82 +5 +-37 +-65 +-82 +-60 +-5 +34 +62 +70 +-1 +-39 +-64 +-47 +4 +41 +67 +73 +0 +-39 +-64 +-48 +4 +41 +67 +72 +0 +-40 +-65 +-48 +3 +40 +66 +72 +-1 +-40 +-65 +-49 +3 +40 +65 +71 +-1 +-41 +-65 +-49 +2 +39 +65 +71 +-2 +-41 +-66 +-50 +2 +39 +64 +70 +-3 +-42 +-66 +-83 +-60 +-4 +36 +64 +83 +84 +8 +-34 +-62 +-80 +-58 +-3 +36 +64 +83 +84 +7 +-36 +-64 +-81 +-59 +-5 +35 +63 +82 +83 +6 +-36 +-63 +-82 +-59 +-5 +35 +63 +81 +83 +6 +-37 +-64 +-81 +-60 +-4 +35 +63 +82 +83 +7 +-36 +-64 +-49 +1 +37 +62 +68 +-5 +-44 +-69 +-52 +-1 +36 +62 +68 +-4 +-43 +-69 +-52 +0 +37 +63 +68 +-4 +-43 +-68 +-51 +0 +37 +63 +69 +-3 +-43 +-67 +-51 +1 +38 +64 +69 +-3 +-42 +-67 +-50 +1 +38 +64 +70 +-3 +-42 +-66 +-82 +-59 +-3 +37 +64 +83 +84 +9 +-34 +-62 +-79 +-57 +-3 +37 +64 +83 +85 +8 +-35 +-63 +-81 +-58 +-4 +36 +64 +82 +84 +7 +-35 +-63 +-81 +-59 +-4 +35 +63 +81 +83 +7 +-37 +-64 +-82 +-60 +-5 +34 +62 +81 +82 +6 +-36 +-65 +-50 +1 +37 +62 +68 +-5 +-44 +-69 +-52 +0 +36 +62 +69 +-4 +-43 +-68 +-52 +1 +37 +63 +69 +-4 +-42 +-67 +-51 +1 +38 +64 +70 +-4 +-42 +-67 +-51 +1 +38 +64 +70 +-3 +-42 +-67 +-51 +2 +38 +64 +70 +-3 +-42 +-68 +-51 +1 +37 +64 +80 +80 +3 +-40 +-68 +-85 +-64 +-8 +31 +59 +78 +80 +3 +-39 +-68 +-85 +-63 +-7 +33 +60 +79 +81 +4 +-38 +-66 +-84 +-62 +-6 +33 +61 +79 +81 +5 +-38 +-65 +-83 +-62 +-6 +33 +61 +80 +81 +5 +-37 +-65 +-83 +-61 +-6 +34 +61 +70 +-2 +-40 +-64 +-48 +4 +40 +66 +72 +-1 +-40 +-65 +-49 +3 +40 +65 +71 +-2 +-42 +-66 +-50 +2 +39 +64 +70 +-3 +-42 +-66 +-50 +1 +38 +64 +70 +-3 +-42 +-67 +-51 +1 +38 +64 +70 +-3 +-42 +-67 +-51 +1 +38 +64 +80 +81 +3 +-39 +-67 +-85 +-63 +-7 +32 +60 +79 +80 +4 +-38 +-66 +-84 +-62 +-6 +33 +61 +80 +82 +5 +-37 +-65 +-83 +-61 +-5 +34 +62 +81 +83 +5 +-37 +-64 +-82 +-60 +-5 +35 +63 +81 +83 +6 +-36 +-64 +-81 +-59 +-4 +35 +63 +81 +83 +6 +-36 +-64 +-82 +-60 +-4 +34 +63 +81 +83 +7 +-36 +-64 +-81 +-60 +-4 +35 +63 +81 +83 +6 +-36 +-64 +-82 +-60 +-5 +35 +63 +81 +83 +6 +-36 +-64 +-82 +-60 +-5 +34 +62 +81 +82 +6 +-37 +-65 +-82 +-61 +-5 +34 +62 +70 +-1 +-40 +-64 +-48 +4 +40 +66 +72 +-1 +-40 +-65 +-49 +3 +39 +65 +71 +-2 +-41 +-66 +-50 +2 +39 +64 +70 +-2 +-42 +-67 +-51 +1 +38 +64 +70 +-3 +-42 +-66 +-51 +1 +38 +64 +70 +-3 +-42 +-67 +-51 +2 +38 +64 +70 +-3 +-42 +-67 +-51 +1 +38 +64 +70 +-3 +-42 +-67 +-51 +1 +38 +64 +70 +-2 +-42 +-66 +-50 +2 +39 +64 +70 +-2 +-42 +-66 +-50 +2 +39 +64 +70 +-2 +-42 +-66 +-50 +2 +39 +64 +70 +-2 +-42 +-67 +-50 +2 +38 +64 +70 +-2 +-41 +-66 +-83 +-59 +-3 +37 +65 +84 +85 +9 +-34 +-62 +-80 +-58 +-3 +37 +64 +83 +84 +7 +-35 +-63 +-81 +-59 +-4 +36 +63 +82 +83 +6 +-36 +-63 +-81 +-59 +-5 +35 +63 +81 +83 +6 +-37 +-64 +-83 +-60 +-5 +34 +62 +81 +82 +6 +-38 +-65 +-50 +0 +37 +62 +67 +-5 +-44 +-69 +-52 +-1 +36 +62 +68 +-4 +-43 +-68 +-52 +0 +37 +63 +69 +-4 +-43 +-68 +-51 +1 +38 +64 +69 +-3 +-42 +-67 +-51 +1 +38 +64 +70 +-3 +-42 +-67 +-50 +2 +38 +64 +70 +-3 +-42 +-67 +-50 +2 +38 +64 +80 +81 +4 +-39 +-67 +-85 +-63 +-8 +31 +60 +78 +79 +3 +-39 +-67 +-84 +-63 +-7 +33 +60 +79 +81 +4 +-38 +-66 +-83 +-61 +-6 +34 +62 +80 +82 +5 +-37 +-64 +-82 +-60 +-5 +34 +63 +81 +82 +6 +-36 +-64 +-82 +-61 +-5 +34 +62 +70 +-2 +-40 +-64 +-49 +3 +40 +66 +72 +-1 +-41 +-65 +-49 +2 +39 +65 +71 +-2 +-41 +-65 +-49 +3 +40 +65 +71 +-1 +-40 +-65 +-49 +3 +40 +65 +71 +-1 +-41 +-66 +-49 +2 +39 +65 +71 +-1 +-41 +-66 +-50 +2 +39 +64 +81 +82 +4 +-39 +-67 +-85 +-62 +-7 +33 +61 +79 +81 +4 +-38 +-65 +-83 +-61 +-5 +34 +62 +80 +82 +6 +-37 +-64 +-82 +-61 +-5 +34 +63 +81 +83 +6 +-36 +-64 +-82 +-60 +-5 +35 +63 +81 +83 +6 +-36 +-65 +-82 +-60 +-5 +34 +62 +70 +-1 +-40 +-65 +-48 +3 +40 +66 +72 +-1 +-40 +-65 +-49 +3 +39 +65 +71 +-2 +-41 +-66 +-50 +2 +39 +65 +70 +-2 +-41 +-66 +-50 +2 +38 +64 +70 +-3 +-42 +-67 +-50 +1 +38 +64 +70 +-3 +-42 +-67 +-51 +1 +38 +64 +70 +-2 +-42 +-66 +-83 +-59 +-4 +36 +64 +83 +84 +8 +-34 +-62 +-80 +-58 +-4 +36 +63 +82 +83 +6 +-36 +-64 +-82 +-59 +-5 +35 +62 +81 +82 +6 +-37 +-64 +-83 +-60 +-5 +35 +63 +81 +83 +6 +-37 +-64 +-81 +-59 +-4 +35 +63 +82 +83 +7 +-36 +-63 +-48 +2 +38 +64 +69 +-4 +-43 +-68 +-52 +0 +37 +63 +69 +-3 +-42 +-68 +-51 +1 +38 +64 +69 +-3 +-42 +-67 +-51 +1 +38 +64 +70 +-3 +-41 +-67 +-50 +2 +38 +64 +70 +-3 +-42 +-68 +-51 +1 +37 +64 +70 +-3 +-42 +-67 +-83 +-59 +-4 +37 +64 +83 +84 +8 +-35 +-62 +-80 +-58 +-4 +35 +63 +82 +83 +6 +-37 +-64 +-82 +-60 +-5 +34 +62 +81 +82 +6 +-37 +-65 +-83 +-61 +-6 +34 +62 +81 +82 +6 +-37 +-64 +-82 +-60 +-6 +34 +62 +81 +82 +6 +-37 +-64 +-50 +1 +37 +62 +68 +-5 +-45 +-69 +-53 +-1 +36 +62 +68 +-4 +-43 +-68 +-52 +1 +38 +63 +70 +-3 +-42 +-67 +-51 +2 +39 +64 +70 +-2 +-41 +-66 +-50 +2 +39 +64 +70 +-2 +-41 +-66 +-50 +2 +38 +63 +70 +-3 +-42 +-67 +-51 +1 +37 +63 +80 +80 +2 +-40 +-68 +-85 +-64 +-9 +31 +59 +77 +79 +2 +-40 +-68 +-85 +-63 +-8 +31 +60 +78 +80 +3 +-40 +-67 +-84 +-63 +-7 +33 +61 +80 +82 +5 +-37 +-65 +-83 +-61 +-6 +34 +62 +80 +82 +5 +-37 +-65 +-83 +-61 +-5 +34 +63 +71 +-1 +-38 +-63 +-47 +5 +41 +67 +73 +0 +-39 +-64 +-48 +4 +40 +66 +72 +-1 +-40 +-66 +-49 +2 +39 +65 +71 +-2 +-41 +-66 +-50 +2 +39 +65 +71 +-2 +-41 +-66 +-49 +2 +39 +65 +71 +-1 +-41 +-66 +-49 +2 +39 +65 +81 +82 +4 +-38 +-66 +-84 +-62 +-6 +33 +61 +80 +82 +5 +-37 +-65 +-83 +-61 +-5 +34 +62 +81 +82 +5 +-37 +-65 +-82 +-60 +-5 +34 +62 +80 +82 +5 +-37 +-65 +-83 +-61 +-6 +34 +62 +80 +82 +5 +-37 +-65 +-82 +-61 +-6 +34 +62 +70 +-1 +-40 +-65 +-48 +4 +40 +66 +71 +-1 +-40 +-65 +-49 +3 +39 +65 +71 +-1 +-41 +-66 +-50 +2 +39 +65 +71 +-2 +-41 +-66 +-49 +3 +39 +65 +71 +-2 +-41 +-66 +-49 +2 +39 +65 +71 +-2 +-41 +-66 +-49 +2 +39 +65 +71 +-2 +-42 +-66 +-82 +-59 +-3 +37 +65 +84 +85 +8 +-34 +-62 +-80 +-58 +-3 +37 +64 +82 +84 +7 +-36 +-63 +-81 +-59 +-4 +35 +63 +81 +82 +6 +-37 +-64 +-82 +-61 +-5 +34 +62 +81 +82 +6 +-37 +-65 +-82 +-61 +-5 +35 +62 +81 +82 +6 +-37 +-64 +-50 +1 +37 +62 +68 +-5 +-44 +-69 +-52 +0 +36 +62 +69 +-4 +-43 +-67 +-52 +1 +37 +63 +69 +-4 +-42 +-68 +-51 +1 +37 +63 +69 +-4 +-42 +-67 +-51 +1 +37 +63 +69 +-3 +-42 +-67 +-51 +1 +37 +63 +69 +-4 +-43 +-68 +-84 +-60 +-5 +35 +63 +82 +83 +7 +-36 +-63 +-82 +-59 +-4 +35 +63 +82 +83 +7 +-36 +-63 +-81 +-59 +-4 +35 +63 +82 +83 +7 +-36 +-63 +-81 +-59 +-5 +35 +63 +82 +83 +6 +-36 +-64 +-82 +-60 +-5 +35 +62 +81 +82 +6 +-37 +-64 +-50 +1 +37 +62 +68 +-5 +-44 +-69 +-53 +-1 +36 +62 +68 +-5 +-44 +-68 +-53 +0 +37 +62 +69 +-4 +-43 +-68 +-52 +0 +37 +63 +69 +-3 +-43 +-67 +-51 +1 +38 +64 +70 +-3 +-42 +-67 +-51 +2 +39 +64 +70 +-2 +-42 +-66 +-50 +2 +39 +65 +81 +82 +4 +-38 +-66 +-84 +-62 +-7 +33 +61 +79 +81 +5 +-38 +-65 +-83 +-61 +-6 +33 +62 +80 +82 +5 +-38 +-65 +-82 +-61 +-5 +34 +62 +80 +81 +5 +-37 +-65 +-82 +-61 +-5 +34 +62 +81 +82 +5 +-37 +-65 +-82 +-60 +-5 +34 +62 +69 +-1 +-40 +-64 +-48 +4 +41 +67 +73 +0 +-39 +-64 +-47 +4 +41 +67 +72 +0 +-39 +-65 +-48 +3 +40 +66 +71 +-1 +-41 +-66 +-50 +2 +39 +64 +70 +-3 +-42 +-67 +-51 +1 +38 +64 +70 +-3 +-43 +-67 +-50 +1 +38 +64 +80 +81 +3 +-39 +-67 +-85 +-63 +-8 +32 +61 +79 +81 +4 +-38 +-65 +-84 +-61 +-6 +33 +62 +80 +82 +5 +-37 +-65 +-83 +-61 +-5 +34 +62 +81 +82 +5 +-37 +-65 +-82 +-61 +-5 +34 +62 +80 +82 +5 +-37 +-65 +-83 +-61 +-6 +34 +62 +69 +-2 +-40 +-66 +-49 +3 +40 +66 +71 +-2 +-40 +-66 +-49 +2 +39 +65 +70 +-2 +-42 +-67 +-50 +2 +39 +65 +70 +-2 +-41 +-67 +-50 +2 +38 +64 +70 +-2 +-41 +-67 +-50 +2 +38 +65 +70 +-2 +-41 +-66 +-50 +2 +39 +65 +70 +-2 +-41 +-66 +-82 +-58 +-3 +37 +65 +84 +85 +8 +-34 +-62 +-80 +-58 +-4 +36 +64 +82 +84 +7 +-36 +-63 +-81 +-59 +-4 +35 +63 +81 +83 +6 +-37 +-64 +-82 +-60 +-5 +35 +62 +81 +82 +6 +-37 +-64 +-82 +-60 +-5 +35 +62 +81 +83 +6 +-36 +-64 +-49 +1 +37 +63 +68 +-5 +-43 +-68 +-52 +0 +37 +63 +69 +-4 +-43 +-68 +-52 +1 +37 +63 +69 +-4 +-43 +-67 +-51 +1 +37 +63 +70 +-3 +-42 +-67 +-51 +1 +38 +63 +69 +-4 +-43 +-67 +-51 +1 +38 +63 +70 +-3 +-42 +-67 +-83 +-60 +-4 +36 +64 +83 +84 +8 +-35 +-62 +-80 +-59 +-4 +36 +63 +82 +83 +7 +-36 +-63 +-81 +-60 +-4 +35 +63 +82 +83 +6 +-36 +-64 +-82 +-60 +-5 +35 +62 +81 +83 +6 +-37 +-64 +-82 +-60 +-5 +35 +63 +81 +83 +6 +-37 +-64 +-49 +1 +37 +62 +68 +-5 +-44 +-69 +-52 +-1 +37 +62 +68 +-4 +-43 +-68 +-51 +0 +37 +63 +69 +-3 +-42 +-67 +-51 +1 +38 +64 +70 +-2 +-42 +-67 +-50 +2 +39 +64 +70 +-2 +-42 +-66 +-50 +2 +39 +64 +70 +-2 +-42 +-66 +-50 +1 +38 +64 +80 +80 +3 +-39 +-68 +-85 +-64 +-9 +31 +59 +77 +79 +3 +-40 +-67 +-85 +-63 +-7 +32 +60 +79 +80 +4 +-38 +-66 +-84 +-62 +-6 +33 +61 +80 +81 +4 +-38 +-66 +-83 +-61 +-6 +33 +62 +79 +82 +5 +-37 +-65 +-83 +-61 +-5 +33 +62 +80 +82 +5 +-37 +-65 +-82 +-60 +-5 +35 +63 +81 +83 +6 +-36 +-65 +-82 +-60 +-5 +35 +63 +81 +83 +6 +-36 +-64 +-82 +-60 +-5 +34 +62 +80 +82 +5 +-37 +-65 +-83 +-61 +-6 +33 +62 +80 +81 +5 +-37 +-65 +-82 +-61 +-5 +34 +62 +70 +-1 +-40 +-64 +-48 +4 +41 +66 +72 +-1 +-40 +-65 +-48 +3 +40 +66 +72 +-1 +-40 +-65 +-48 +3 +40 +66 +72 +0 +-40 +-65 +-48 +3 +40 +66 +71 +-1 +-41 +-66 +-49 +2 +39 +65 +71 +-2 +-41 +-66 +-50 +2 +39 +65 +81 +81 +3 +-39 +-67 +-85 +-63 +-7 +32 +60 +78 +80 +3 +-39 +-67 +-85 +-63 +-7 +32 +60 +79 +80 +4 +-38 +-66 +-84 +-62 +-7 +33 +60 +79 +81 +4 +-38 +-66 +-83 +-61 +-6 +34 +61 +80 +82 +5 +-38 +-66 +-83 +-61 +-6 +34 +62 +69 +-2 +-40 +-65 +-48 +4 +40 +66 +72 +-1 +-40 +-65 +-49 +3 +39 +65 +71 +-1 +-41 +-66 +-49 +2 +39 +65 +71 +-2 +-41 +-66 +-50 +2 +39 +65 +71 +-2 +-41 +-66 +-49 +2 +39 +65 +71 +-2 +-41 +-66 +-49 +3 +40 +66 +71 +-1 +-40 +-65 +-49 +3 +40 +66 +71 +-1 +-40 +-65 +-48 +3 +40 +66 +71 +-1 +-40 +-65 +-49 +3 +40 +65 +71 +-2 +-41 +-66 +-49 +2 +39 +64 +70 +-2 +-42 +-67 +-51 +1 +38 +64 +70 +-3 +-43 +-67 +-51 +1 +38 +64 +70 +-3 +-42 +-67 +-83 +-60 +-4 +36 +64 +83 +84 +8 +-35 +-62 +-80 +-58 +-3 +37 +64 +83 +84 +7 +-35 +-63 +-81 +-59 +-5 +36 +63 +82 +83 +6 +-37 +-64 +-82 +-60 +-5 +35 +62 +81 +82 +6 +-37 +-64 +-82 +-61 +-5 +34 +62 +81 +82 +6 +-37 +-65 +-83 +-61 +-6 +34 +62 +81 +82 +5 +-37 +-65 +-82 +-60 +-6 +34 +62 +80 +82 +5 +-37 +-64 +-83 +-60 +-5 +34 +62 +81 +82 +6 +-37 +-64 +-83 +-61 +-6 +34 +62 +81 +82 +6 +-37 +-65 +-82 +-60 +-5 +35 +62 +81 +82 +6 +-37 +-65 +-50 +1 +37 +63 +68 +-5 +-43 +-69 +-52 +0 +37 +63 +69 +-4 +-43 +-68 +-51 +1 +37 +63 +69 +-3 +-42 +-67 +-51 +1 +37 +64 +69 +-3 +-42 +-67 +-51 +1 +38 +64 +70 +-3 +-42 +-68 +-51 +1 +38 +64 +70 +-3 +-42 +-67 +-50 +2 +38 +64 +80 +81 +3 +-39 +-67 +-85 +-63 +-7 +32 +60 +79 +80 +4 +-38 +-66 +-83 +-62 +-6 +33 +61 +80 +82 +5 +-38 +-65 +-83 +-61 +-6 +34 +62 +80 +82 +5 +-37 +-65 +-83 +-61 +-5 +34 +62 +80 +82 +6 +-37 +-65 +-82 +-60 +-5 +34 +62 +70 +-1 +-40 +-64 +-48 +4 +41 +66 +72 +0 +-40 +-65 +-49 +3 +40 +65 +71 +-2 +-41 +-66 +-50 +2 +39 +64 +70 +-2 +-42 +-67 +-50 +1 +38 +64 +70 +-3 +-42 +-67 +-51 +1 +38 +64 +70 +-3 +-42 +-67 +-51 +1 +38 +64 +70 +-3 +-42 +-66 +-50 +2 +39 +64 +70 +-3 +-42 +-66 +-50 +2 +39 +64 +71 +-2 +-42 +-66 +-50 +2 +38 +64 +70 +-3 +-42 +-67 +-51 +2 +38 +64 +70 +-3 +-42 +-66 +-50 +2 +38 +64 +71 +-2 +-41 +-66 +-51 +2 +38 +64 +70 +-3 +-42 +-67 +-83 +-60 +-4 +36 +64 +82 +84 +8 +-35 +-63 +-81 +-59 +-4 +36 +63 +82 +83 +7 +-36 +-64 +-82 +-60 +-5 +35 +62 +81 +82 +6 +-37 +-65 +-82 +-60 +-5 +35 +62 +81 +83 +6 +-37 +-64 +-82 +-60 +-5 +35 +63 +82 +83 +7 +-36 +-63 +-81 +-59 +-4 +35 +63 +82 +83 +7 +-36 +-63 +-81 +-59 +-4 +36 +63 +82 +83 +7 +-36 +-64 +-82 +-59 +-5 +35 +62 +81 +83 +6 +-36 +-64 +-82 +-60 +-5 +35 +62 +81 +82 +6 +-37 +-65 +-83 +-61 +-6 +34 +61 +80 +81 +5 +-38 +-65 +-50 +0 +36 +61 +67 +-6 +-45 +-70 +-53 +-2 +35 +61 +67 +-5 +-44 +-69 +-52 +-1 +36 +62 +68 +-4 +-43 +-68 +-51 +0 +37 +63 +69 +-4 +-43 +-68 +-51 +0 +37 +63 +69 +-3 +-42 +-68 +-51 +1 +38 +64 +69 +-3 +-42 +-67 +-50 +2 +38 +64 +70 +-2 +-42 +-67 +-50 +2 +39 +65 +70 +-2 +-41 +-66 +-50 +2 +39 +65 +70 +-2 +-41 +-66 +-50 +2 +39 +65 +71 +-2 +-41 +-66 +-50 +2 +39 +65 +71 +-2 +-41 +-66 +-50 +2 +38 +64 +70 +-2 +-41 +-67 +-50 +2 +38 +64 +81 +81 +3 +-40 +-67 +-85 +-64 +-8 +31 +59 +78 +79 +3 +-39 +-67 +-85 +-63 +-7 +32 +60 +79 +80 +4 +-38 +-66 +-84 +-62 +-7 +33 +61 +80 +82 +4 +-38 +-65 +-83 +-61 +-6 +33 +62 +80 +82 +5 +-37 +-65 +-83 +-61 +-5 +34 +63 +81 +82 +6 +-36 +-64 +-81 +-60 +-4 +35 +63 +81 +83 +6 +-36 +-64 +-81 +-60 +-5 +35 +63 +81 +83 +6 +-36 +-64 +-82 +-60 +-4 +35 +63 +81 +83 +6 +-37 +-64 +-82 +-60 +-4 +35 +63 +81 +83 +6 +-36 +-64 +-82 +-60 +-5 +35 +62 +71 +-1 +-40 +-64 +-48 +4 +41 +67 +73 +0 +-39 +-64 +-48 +3 +40 +66 +72 +-1 +-41 +-65 +-49 +2 +39 +65 +71 +-2 +-41 +-66 +-50 +1 +38 +64 +69 +-3 +-42 +-67 +-51 +1 +37 +63 +69 +-4 +-43 +-68 +-52 +0 +37 +63 +69 +-3 +-43 +-68 +-51 +0 +38 +64 +69 +-3 +-43 +-68 +-51 +1 +38 +65 +71 +-2 +-42 +-69 +-52 +0 +37 +65 +71 +-1 +-41 +-67 +-52 +-1 +37 +64 +71 +-1 +-40 +-66 +-50 +1 +38 +64 +70 +-2 +-41 +-67 +-50 +1 +39 +65 +71 +-3 +-43 +-67 +-83 +-60 +-3 +38 +66 +84 +84 +7 +-36 +-64 +-81 +-59 +-4 +36 +63 +82 +83 +7 +-36 +-63 +-82 +-61 +-6 +35 +64 +83 +84 +8 +-36 +-65 +-83 +-61 +-5 +36 +64 +83 +83 +6 +-38 +-66 +-83 +-60 +-4 +36 +64 +82 +82 +6 +-37 +-64 +-50 +1 +37 +63 +68 +-4 +-43 +-68 +-52 +1 +37 +63 +69 +-4 +-43 +-68 +-51 +1 +37 +63 +69 +-3 +-42 +-67 +-51 +1 +38 +64 +70 +-2 +-41 +-66 +-51 +2 +39 +64 +70 +-2 +-42 +-67 +-50 +2 +39 +64 +70 +-2 +-42 +-66 +-83 +-59 +-3 +37 +65 +84 +85 +9 +-35 +-62 +-80 +-58 +-3 +37 +64 +83 +84 +7 +-35 +-63 +-82 +-59 +-5 +35 +63 +82 +83 +7 +-36 +-63 +-82 +-60 +-5 +35 +63 +82 +83 +6 +-37 +-64 +-82 +-60 +-5 +34 +62 +81 +82 +6 +-37 +-65 +-82 +-60 +-5 +35 +62 +82 +83 +6 +-36 +-64 +-82 +-60 +-5 +35 +63 +81 +83 +6 +-37 +-64 +-82 +-60 +-5 +35 +63 +81 +82 +6 +-37 +-64 +-82 +-60 +-5 +35 +62 +81 +82 +6 +-37 +-65 +-82 +-60 +-5 +35 +62 +81 +83 +6 +-37 +-64 +-50 +1 +37 +62 +68 +-5 +-44 +-69 +-53 +-1 +36 +62 +68 +-4 +-44 +-68 +-52 +0 +37 +63 +69 +-4 +-43 +-68 +-52 +1 +37 +63 +69 +-4 +-43 +-68 +-52 +0 +37 +63 +69 +-4 +-43 +-68 +-51 +0 +37 +63 +69 +-4 +-43 +-68 +-52 +0 +37 +63 +79 +80 +3 +-40 +-68 +-86 +-64 +-8 +31 +59 +78 +80 +3 +-40 +-67 +-84 +-63 +-7 +33 +60 +79 +81 +4 +-38 +-66 +-83 +-61 +-6 +34 +61 +80 +82 +5 +-37 +-65 +-83 +-61 +-6 +33 +62 +80 +82 +5 +-37 +-65 +-83 +-61 +-6 +33 +61 +69 +-2 +-40 +-65 +-49 +4 +40 +66 +72 +-1 +-40 +-66 +-50 +2 +39 +65 +71 +-2 +-41 +-67 +-50 +2 +38 +64 +70 +-3 +-41 +-66 +-50 +2 +39 +64 +71 +-2 +-41 +-66 +-50 +2 +39 +65 +71 +-3 +-42 +-66 +-50 +2 +39 +65 +71 +-2 +-40 +-65 +-49 +3 +39 +65 +71 +-2 +-40 +-65 +-49 +3 +40 +65 +71 +-2 +-41 +-65 +-49 +3 +39 +65 +71 +-2 +-41 +-66 +-50 +3 +39 +65 +71 +-1 +-41 +-66 +-50 +3 +39 +65 +71 +-1 +-41 +-66 +-50 +2 +39 +64 +71 +-2 +-41 +-66 +-82 +-59 +-3 +37 +65 +84 +85 +9 +-34 +-61 +-79 +-58 +-3 +37 +64 +83 +84 +7 +-35 +-63 +-81 +-59 +-4 +36 +63 +82 +83 +6 +-37 +-64 +-82 +-60 +-5 +34 +62 +81 +82 +6 +-36 +-64 +-82 +-61 +-5 +34 +61 +80 +82 +5 +-37 +-65 +-51 +0 +36 +62 +67 +-6 +-45 +-70 +-53 +-1 +36 +63 +69 +-5 +-45 +-70 +-53 +-1 +36 +63 +70 +-3 +-43 +-69 +-53 +-1 +36 +63 +71 +-2 +-42 +-67 +-52 +0 +37 +63 +70 +-2 +-41 +-66 +-51 +1 +38 +63 +69 +-3 +-42 +-67 +-83 +-60 +-4 +37 +65 +84 +83 +7 +-36 +-63 +-81 +-59 +-4 +36 +63 +82 +83 +7 +-36 +-64 +-82 +-61 +-6 +35 +63 +82 +84 +7 +-37 +-65 +-84 +-62 +-5 +35 +64 +83 +83 +6 +-39 +-66 +-84 +-61 +-5 +36 +63 +82 +82 +5 +-38 +-65 +-50 +0 +36 +62 +67 +-5 +-45 +-70 +-53 +-1 +35 +62 +67 +-5 +-44 +-69 +-53 +-1 +35 +62 +68 +-5 +-44 +-69 +-52 +0 +36 +62 +68 +-4 +-43 +-69 +-52 +0 +37 +63 +69 +-4 +-43 +-68 +-51 +1 +37 +63 +69 +-3 +-42 +-67 +-51 +1 +38 +64 +80 +80 +3 +-40 +-67 +-85 +-64 +-7 +32 +60 +79 +80 +4 +-38 +-66 +-84 +-62 +-7 +33 +62 +80 +82 +5 +-37 +-64 +-82 +-60 +-4 +35 +63 +81 +83 +7 +-36 +-64 +-81 +-59 +-4 +35 +64 +82 +83 +7 +-36 +-64 +-81 +-60 +-4 +35 +63 +82 +83 +7 +-35 +-64 +-81 +-60 +-5 +35 +63 +81 +83 +5 +-37 +-65 +-83 +-61 +-5 +34 +62 +81 +82 +6 +-37 +-64 +-82 +-61 +-5 +34 +62 +81 +82 +5 +-37 +-65 +-82 +-61 +-5 +34 +62 +81 +83 +5 +-37 +-65 +-83 +-60 +-5 +34 +62 +70 +-1 +-40 +-65 +-48 +4 +40 +66 +72 +-1 +-40 +-66 +-49 +3 +39 +65 +71 +-2 +-41 +-67 +-50 +2 +38 +65 +70 +-2 +-41 +-66 +-50 +2 +39 +65 +71 +-2 +-41 +-66 +-50 +2 +39 +65 +71 +-2 +-41 +-66 +-50 +3 +39 +65 +71 +-2 +-40 +-65 +-49 +3 +39 +65 +72 +-2 +-40 +-65 +-49 +3 +39 +65 +71 +-2 +-40 +-66 +-49 +3 +39 +65 +71 +-2 +-41 +-66 +-50 +2 +39 +65 +71 +-2 +-41 +-66 +-50 +2 +38 +64 +70 +-3 +-41 +-67 +-50 +2 +38 +64 +70 +-3 +-41 +-66 +-83 +-59 +-3 +37 +65 +84 +85 +9 +-34 +-61 +-79 +-57 +-2 +37 +64 +83 +84 +8 +-35 +-63 +-81 +-59 +-5 +35 +63 +82 +83 +6 +-37 +-64 +-83 +-60 +-6 +34 +62 +80 +82 +5 +-38 +-65 +-83 +-61 +-6 +34 +61 +80 +81 +5 +-38 +-65 +-51 +0 +36 +61 +67 +-6 +-45 +-70 +-53 +-2 +35 +61 +67 +-5 +-44 +-69 +-53 +-1 +36 +62 +68 +-4 +-43 +-68 +-51 +0 +37 +64 +69 +-3 +-42 +-67 +-50 +1 +38 +64 +70 +-3 +-42 +-67 +-50 +2 +38 +65 +71 +-2 +-41 +-66 +-82 +-59 +-4 +37 +65 +84 +85 +9 +-34 +-62 +-80 +-57 +-3 +37 +64 +83 +84 +8 +-35 +-62 +-80 +-58 +-3 +36 +64 +83 +84 +7 +-36 +-63 +-81 +-59 +-4 +36 +63 +82 +83 +6 +-36 +-64 +-82 +-60 +-5 +35 +63 +81 +83 +6 +-37 +-64 +-50 +1 +37 +62 +67 +-6 +-45 +-70 +-53 +-2 +35 +61 +67 +-5 +-45 +-69 +-53 +-1 +36 +61 +67 +-5 +-45 +-69 +-53 +-1 +36 +62 +68 +-4 +-44 +-68 +-52 +0 +37 +62 +68 +-4 +-43 +-68 +-51 +0 +37 +63 +69 +-3 +-42 +-67 +-51 +1 +38 +64 +80 +80 +3 +-39 +-67 +-85 +-63 +-7 +32 +60 +79 +80 +4 +-38 +-66 +-84 +-62 +-6 +34 +61 +80 +82 +5 +-37 +-64 +-82 +-60 +-5 +35 +63 +81 +83 +7 +-36 +-64 +-81 +-59 +-4 +35 +64 +82 +83 +7 +-35 +-63 +-81 +-59 +-4 +35 +63 +71 +-1 +-39 +-64 +-48 +4 +40 +66 +72 +-1 +-40 +-65 +-50 +3 +40 +65 +71 +-2 +-41 +-66 +-50 +2 +38 +64 +70 +-3 +-42 +-67 +-51 +1 +37 +63 +69 +-4 +-42 +-67 +-51 +1 +38 +63 +70 +-3 +-42 +-67 +-51 +1 +38 +63 +80 +80 +3 +-40 +-68 +-86 +-64 +-8 +31 +60 +78 +80 +3 +-39 +-66 +-84 +-61 +-6 +33 +61 +80 +81 +5 +-37 +-65 +-83 +-61 +-5 +34 +62 +81 +82 +6 +-36 +-64 +-82 +-60 +-5 +35 +63 +81 +83 +6 +-36 +-64 +-82 +-60 +-5 +35 +63 +71 +0 +-39 +-64 +-47 +5 +42 +67 +73 +0 +-39 +-64 +-48 +4 +40 +66 +72 +-1 +-40 +-66 +-49 +3 +39 +65 +71 +-1 +-40 +-66 +-49 +3 +39 +65 +71 +-2 +-41 +-66 +-50 +2 +38 +64 +70 +-2 +-42 +-67 +-50 +2 +38 +64 +70 +-3 +-42 +-67 +-51 +1 +38 +64 +70 +-3 +-42 +-67 +-50 +2 +38 +64 +70 +-3 +-42 +-67 +-51 +1 +38 +64 +70 +-3 +-42 +-67 +-50 +2 +38 +65 +71 +-2 +-41 +-66 +-50 +2 +39 +65 +71 +-2 +-41 +-66 +-50 +2 +39 +65 +71 +-2 +-41 +-66 +-50 +2 +39 +65 +71 +-2 +-41 +-66 +-50 +2 +38 +64 +70 +-3 +-42 +-67 +-51 +1 +37 +63 +69 +-4 +-42 +-67 +-51 +1 +38 +63 +69 +-3 +-43 +-67 +-51 +1 +38 +63 +69 +-4 +-43 +-68 +-52 +0 +37 +63 +69 +-3 +-43 +-68 +-52 +0 +37 +63 +79 +80 +2 +-41 +-68 +-86 +-64 +-9 +31 +59 +78 +80 +3 +-39 +-67 +-84 +-63 +-7 +32 +60 +79 +81 +4 +-38 +-66 +-83 +-61 +-6 +34 +62 +80 +82 +5 +-38 +-65 +-83 +-61 +-5 +34 +62 +80 +82 +5 +-37 +-65 +-83 +-61 +-6 +34 +62 +81 +82 +5 +-36 +-64 +-82 +-60 +-5 +35 +62 +81 +83 +6 +-36 +-64 +-82 +-60 +-5 +35 +63 +81 +83 +6 +-36 +-64 +-82 +-60 +-4 +35 +63 +82 +83 +7 +-36 +-63 +-80 +-59 +-4 +35 +63 +81 +83 +6 +-36 +-64 +-82 +-60 +-5 +35 +63 +81 +83 +6 +-36 +-64 +-82 +-60 +-5 +34 +62 +81 +82 +5 +-37 +-65 +-82 +-61 +-5 +34 +62 +80 +82 +5 +-37 +-66 +-83 +-61 +-6 +34 +62 +80 +82 +5 +-37 +-65 +-83 +-61 +-6 +33 +62 +80 +82 +5 +-38 +-65 +-83 +-61 +-6 +33 +62 +70 +-2 +-40 +-65 +-48 +4 +41 +66 +72 +-1 +-40 +-65 +-49 +3 +39 +65 +71 +-2 +-41 +-66 +-50 +2 +39 +64 +71 +-2 +-41 +-66 +-50 +2 +39 +64 +71 +-2 +-42 +-66 +-50 +2 +39 +64 +70 +-3 +-42 +-67 +-51 +1 +38 +64 +80 +81 +3 +-39 +-67 +-85 +-63 +-8 +32 +60 +78 +80 +3 +-39 +-67 +-84 +-62 +-6 +33 +62 +80 +81 +5 +-37 +-65 +-83 +-61 +-5 +34 +62 +80 +82 +5 +-37 +-65 +-82 +-61 +-5 +34 +63 +81 +83 +6 +-36 +-64 +-81 +-60 +-4 +35 +63 +82 +83 +7 +-35 +-64 +-81 +-59 +-4 +36 +63 +82 +83 +7 +-35 +-64 +-81 +-59 +-4 +35 +63 +81 +83 +6 +-36 +-64 +-81 +-59 +-4 +35 +63 +81 +83 +6 +-36 +-64 +-82 +-60 +-5 +34 +62 +81 +82 +5 +-37 +-65 +-82 +-61 +-5 +34 +62 +71 +-1 +-39 +-64 +-48 +4 +41 +66 +72 +-1 +-40 +-65 +-49 +3 +40 +65 +71 +-2 +-41 +-66 +-50 +2 +39 +64 +71 +-3 +-42 +-67 +-51 +2 +38 +64 +70 +-3 +-42 +-67 +-51 +1 +38 +64 +70 +-3 +-43 +-67 +-51 +1 +37 +63 +69 +-4 +-43 +-67 +-51 +1 +38 +63 +69 +-3 +-43 +-67 +-51 +1 +37 +63 +69 +-4 +-43 +-68 +-51 +0 +37 +63 +69 +-4 +-43 +-68 +-51 +0 +37 +63 +69 +-4 +-43 +-68 +-51 +1 +38 +64 +70 +-2 +-42 +-66 +-50 +2 +39 +65 +70 +-2 +-41 +-66 +-82 +-58 +-3 +38 +65 +85 +86 +9 +-34 +-61 +-79 +-57 +-2 +37 +64 +83 +84 +8 +-36 +-63 +-81 +-59 +-4 +35 +63 +81 +82 +6 +-37 +-64 +-83 +-61 +-5 +34 +62 +80 +81 +5 +-38 +-65 +-83 +-61 +-6 +34 +61 +81 +82 +5 +-37 +-65 +-50 +1 +36 +62 +68 +-5 +-44 +-69 +-53 +-1 +36 +62 +68 +-4 +-43 +-68 +-52 +0 +37 +63 +69 +-3 +-43 +-67 +-51 +1 +38 +63 +70 +-3 +-42 +-66 +-50 +2 +38 +64 +71 +-2 +-41 +-66 +-50 +2 +39 +64 +71 +-2 +-41 +-66 +-50 +2 +39 +65 +81 +82 +4 +-38 +-66 +-84 +-62 +-7 +33 +61 +79 +81 +4 +-38 +-65 +-83 +-62 +-6 +33 +61 +80 +81 +5 +-37 +-65 +-83 +-62 +-6 +34 +62 +80 +82 +5 +-37 +-66 +-83 +-61 +-6 +34 +61 +80 +82 +5 +-37 +-65 +-83 +-61 +-5 +34 +62 +70 +-2 +-40 +-65 +-48 +3 +40 +66 +72 +-1 +-40 +-65 +-49 +3 +40 +66 +71 +-1 +-41 +-66 +-49 +2 +39 +65 +71 +-2 +-41 +-66 +-50 +2 +39 +65 +71 +-2 +-41 +-67 +-50 +2 +38 +64 +70 +-3 +-42 +-67 +-50 +1 +38 +64 +80 +81 +3 +-39 +-67 +-85 +-63 +-7 +32 +60 +79 +80 +3 +-39 +-67 +-84 +-62 +-7 +33 +61 +79 +81 +4 +-38 +-66 +-83 +-61 +-6 +34 +62 +80 +82 +5 +-37 +-65 +-83 +-61 +-5 +34 +62 +80 +82 +5 +-37 +-65 +-82 +-61 +-5 +34 +62 +70 +-1 +-40 +-64 +-48 +4 +41 +67 +73 +0 +-40 +-64 +-49 +3 +40 +65 +71 +-1 +-41 +-66 +-50 +2 +39 +65 +71 +-2 +-41 +-66 +-50 +2 +39 +64 +71 +-2 +-41 +-66 +-50 +2 +39 +65 +71 +-2 +-41 +-66 +-50 +2 +39 +64 +70 +-2 +-42 +-67 +-83 +-59 +-3 +37 +65 +84 +85 +9 +-34 +-62 +-80 +-58 +-3 +37 +64 +83 +84 +8 +-35 +-63 +-81 +-59 +-4 +36 +63 +82 +83 +7 +-36 +-63 +-82 +-59 +-4 +35 +63 +81 +83 +6 +-37 +-64 +-82 +-61 +-5 +34 +62 +81 +82 +6 +-37 +-64 +-50 +1 +37 +62 +68 +-5 +-44 +-69 +-53 +-1 +36 +62 +69 +-5 +-43 +-68 +-52 +0 +37 +63 +69 +-4 +-43 +-68 +-52 +0 +37 +63 +69 +-4 +-43 +-68 +-51 +1 +37 +63 +69 +-4 +-43 +-68 +-51 +1 +37 +64 +70 +-3 +-42 +-67 +-84 +-60 +-4 +36 +64 +83 +85 +9 +-34 +-62 +-80 +-58 +-3 +37 +64 +83 +84 +8 +-35 +-62 +-80 +-59 +-3 +37 +64 +83 +84 +8 +-35 +-63 +-81 +-59 +-4 +36 +63 +82 +84 +7 +-36 +-63 +-81 +-59 +-4 +36 +63 +82 +83 +7 +-36 +-63 +-49 +2 +38 +63 +69 +-4 +-43 +-68 +-52 +-1 +37 +63 +69 +-3 +-43 +-68 +-51 +0 +37 +63 +69 +-3 +-43 +-67 +-51 +1 +38 +64 +69 +-3 +-43 +-68 +-51 +0 +37 +63 +69 +-3 +-42 +-67 +-51 +1 +38 +64 +69 +-3 +-42 +-67 +-50 +1 +38 +64 +80 +80 +3 +-40 +-68 +-86 +-64 +-8 +31 +59 +78 +80 +3 +-39 +-67 +-84 +-63 +-7 +32 +60 +79 +81 +4 +-38 +-66 +-84 +-62 +-7 +33 +61 +79 +81 +4 +-38 +-66 +-84 +-62 +-6 +33 +61 +79 +81 +4 +-38 +-66 +-83 +-62 +-6 +33 +61 +69 +-2 +-41 +-65 +-49 +4 +40 +66 +72 +-1 +-40 +-65 +-49 +3 +40 +65 +72 +-1 +-40 +-66 +-49 +3 +40 +65 +71 +-1 +-41 +-65 +-49 +3 +40 +65 +71 +-1 +-41 +-66 +-49 +2 +39 +65 +71 +-2 +-42 +-66 +-50 +2 +39 +64 +81 +81 +4 +-39 +-67 +-84 +-62 +-7 +33 +61 +79 +81 +5 +-38 +-65 +-84 +-61 +-6 +33 +62 +80 +82 +5 +-37 +-65 +-83 +-61 +-5 +34 +62 +81 +82 +6 +-36 +-65 +-81 +-60 +-4 +35 +62 +81 +83 +6 +-36 +-64 +-82 +-60 +-5 +34 +62 +81 +83 +6 +-37 +-64 +-82 +-60 +-5 +34 +63 +81 +83 +6 +-37 +-65 +-82 +-61 +-5 +34 +62 +81 +82 +5 +-37 +-65 +-83 +-61 +-6 +34 +62 +81 +83 +5 +-37 +-64 +-82 +-60 +-5 +34 +63 +81 +83 +6 +-37 +-64 +-82 +-60 +-5 +34 +63 +71 +-1 +-39 +-64 +-48 +4 +40 +66 +72 +0 +-39 +-65 +-49 +4 +40 +66 +72 +-1 +-41 +-66 +-50 +2 +39 +65 +71 +-1 +-41 +-65 +-50 +3 +39 +65 +71 +-2 +-41 +-66 +-50 +2 +39 +65 +71 +-2 +-41 +-66 +-50 +2 +39 +64 +70 +-3 +-42 +-67 +-51 +1 +38 +64 +70 +-2 +-42 +-67 +-51 +1 +38 +64 +70 +-3 +-42 +-67 +-51 +2 +39 +64 +70 +-3 +-42 +-67 +-51 +1 +38 +63 +69 +-3 +-42 +-67 +-51 +1 +38 +63 +70 +-3 +-43 +-67 +-51 +1 +38 +63 +69 +-3 +-43 +-67 +-84 +-60 +-4 +36 +64 +83 +84 +8 +-35 +-63 +-81 +-59 +-3 +36 +64 +83 +84 +8 +-35 +-63 +-81 +-59 +-3 +37 +64 +83 +84 +8 +-35 +-62 +-80 +-58 +-3 +37 +64 +83 +84 +8 +-35 +-62 +-81 +-59 +-4 +36 +64 +82 +83 +7 +-36 +-63 +-49 +2 +38 +63 +68 +-4 +-44 +-69 +-52 +-1 +37 +62 +68 +-4 +-44 +-68 +-52 +0 +37 +62 +69 +-4 +-43 +-68 +-52 +0 +37 +63 +69 +-4 +-43 +-68 +-52 +0 +37 +63 +69 +-4 +-43 +-68 +-52 +0 +37 +62 +69 +-4 +-43 +-68 +-52 +0 +37 +62 +79 +79 +1 +-40 +-69 +-86 +-64 +-9 +30 +59 +77 +79 +2 +-40 +-67 +-85 +-63 +-7 +32 +60 +79 +81 +4 +-38 +-66 +-83 +-62 +-6 +33 +62 +80 +82 +5 +-37 +-65 +-82 +-60 +-5 +34 +62 +81 +83 +5 +-37 +-65 +-82 +-61 +-5 +34 +62 +70 +-1 +-39 +-64 +-47 +4 +41 +67 +73 +0 +-39 +-64 +-47 +4 +41 +67 +72 +0 +-39 +-65 +-48 +3 +40 +66 +72 +-1 +-40 +-65 +-49 +2 +39 +65 +71 +-2 +-41 +-66 +-49 +2 +39 +65 +71 +-2 +-42 +-66 +-50 +2 +39 +65 +81 +81 +3 +-39 +-67 +-85 +-63 +-8 +31 +60 +79 +80 +4 +-38 +-66 +-83 +-62 +-6 +33 +61 +80 +81 +5 +-37 +-66 +-83 +-61 +-6 +33 +61 +80 +82 +4 +-38 +-66 +-84 +-62 +-6 +33 +62 +80 +82 +5 +-37 +-65 +-83 +-61 +-5 +34 +62 +70 +-1 +-39 +-64 +-48 +4 +41 +67 +72 +0 +-39 +-64 +-48 +4 +40 +66 +72 +-1 +-40 +-65 +-49 +3 +40 +66 +72 +-1 +-40 +-66 +-50 +2 +39 +65 +71 +-2 +-41 +-66 +-50 +3 +39 +65 +71 +-2 +-41 +-66 +-50 +2 +38 +64 +70 +-3 +-42 +-67 +-83 +-59 +-4 +36 +64 +83 +85 +8 +-35 +-62 +-80 +-58 +-3 +36 +64 +83 +83 +7 +-36 +-64 +-82 +-60 +-4 +36 +63 +82 +83 +7 +-36 +-64 +-82 +-60 +-5 +35 +62 +81 +83 +6 +-37 +-64 +-82 +-60 +-5 +35 +62 +81 +83 +6 +-37 +-64 +-50 +1 +37 +62 +68 +-5 +-44 +-69 +-53 +-1 +36 +62 +68 +-4 +-43 +-68 +-52 +0 +37 +63 +69 +-3 +-43 +-67 +-51 +1 +38 +63 +69 +-4 +-43 +-67 +-51 +1 +38 +64 +70 +-3 +-42 +-67 +-51 +1 +38 +64 +70 +-3 +-42 +-67 +-84 +-60 +-4 +36 +64 +83 +84 +8 +-34 +-62 +-80 +-58 +-3 +37 +64 +83 +84 +8 +-35 +-63 +-81 +-59 +-4 +36 +63 +82 +83 +7 +-36 +-63 +-81 +-59 +-4 +36 +64 +82 +83 +7 +-36 +-63 +-82 +-60 +-4 +35 +63 +82 +83 +6 +-37 +-64 +-49 +1 +37 +62 +68 +-5 +-45 +-69 +-53 +-2 +36 +61 +67 +-5 +-44 +-69 +-53 +-1 +36 +62 +68 +-5 +-44 +-68 +-52 +0 +37 +63 +69 +-4 +-43 +-68 +-51 +1 +38 +63 +69 +-3 +-42 +-67 +-51 +1 +38 +64 +70 +-2 +-41 +-66 +-50 +2 +39 +65 +81 +82 +4 +-38 +-66 +-84 +-62 +-7 +32 +61 +79 +81 +4 +-38 +-66 +-83 +-62 +-6 +33 +62 +80 +82 +5 +-37 +-65 +-83 +-61 +-5 +34 +62 +81 +82 +6 +-36 +-64 +-81 +-59 +-5 +35 +63 +81 +83 +6 +-36 +-64 +-82 +-60 +-5 +34 +62 +70 +-1 +-40 +-64 +-48 +3 +40 +66 +72 +-1 +-40 +-66 +-49 +2 +39 +65 +70 +-2 +-42 +-66 +-50 +2 +39 +64 +70 +-3 +-42 +-67 +-51 +1 +38 +63 +69 +-3 +-43 +-67 +-51 +1 +38 +64 +70 +-3 +-42 +-66 +-50 +1 +38 +64 +80 +81 +3 +-39 +-67 +-85 +-63 +-8 +32 +60 +78 +80 +3 +-39 +-67 +-84 +-62 +-7 +32 +61 +79 +81 +4 +-38 +-66 +-83 +-62 +-6 +33 +61 +79 +81 +4 +-38 +-66 +-83 +-62 +-6 +34 +62 +80 +82 +5 +-37 +-65 +-82 +-61 +-5 +34 +62 +70 +-1 +-39 +-64 +-47 +4 +41 +67 +72 +0 +-40 +-65 +-48 +3 +40 +66 +72 +-1 +-40 +-65 +-49 +3 +39 +66 +71 +-2 +-40 +-66 +-49 +2 +39 +65 +71 +-2 +-41 +-66 +-50 +2 +39 +65 +70 +-2 +-42 +-66 +-50 +2 +39 +65 +70 +-3 +-42 +-67 +-82 +-60 +-4 +36 +64 +84 +84 +8 +-34 +-62 +-80 +-58 +-3 +36 +63 +82 +83 +7 +-36 +-64 +-82 +-60 +-5 +35 +62 +81 +82 +6 +-37 +-64 +-83 +-60 +-5 +34 +62 +81 +82 +5 +-38 +-65 +-83 +-61 +-6 +34 +61 +80 +81 +5 +-37 +-65 +-51 +0 +35 +61 +67 +-6 +-45 +-70 +-53 +-1 +35 +61 +68 +-5 +-44 +-69 +-52 +0 +37 +63 +69 +-4 +-43 +-67 +-52 +1 +38 +63 +70 +-3 +-42 +-66 +-51 +2 +38 +64 +70 +-2 +-42 +-66 +-50 +2 +39 +64 +71 +-2 +-42 +-66 +-82 +-59 +-3 +37 +65 +84 +85 +9 +-34 +-62 +-80 +-58 +-3 +36 +64 +83 +84 +7 +-36 +-64 +-82 +-60 +-4 +35 +63 +82 +83 +6 +-36 +-64 +-82 +-60 +-6 +34 +62 +81 +82 +5 +-37 +-65 +-83 +-60 +-5 +34 +62 +81 +82 +6 +-37 +-65 +-50 +1 +37 +62 +68 +-5 +-44 +-68 +-52 +0 +37 +63 +69 +-4 +-43 +-68 +-51 +1 +38 +63 +70 +-3 +-43 +-67 +-51 +1 +38 +63 +70 +-3 +-42 +-67 +-51 +1 +38 +64 +70 +-3 +-42 +-67 +-51 +2 +39 +64 +70 +-2 +-42 +-67 +-50 +2 +38 +64 +80 +81 +3 +-39 +-67 +-85 +-63 +-8 +32 +60 +78 +80 +4 +-38 +-66 +-83 +-62 +-6 +33 +62 +80 +82 +5 +-37 +-65 +-83 +-61 +-5 +34 +62 +81 +82 +5 +-37 +-65 +-82 +-60 +-5 +34 +62 +81 +83 +6 +-36 +-64 +-82 +-60 +-5 +34 +63 +70 +-1 +-39 +-64 +-47 +5 +41 +67 +73 +0 +-39 +-64 +-47 +4 +41 +67 +72 +0 +-40 +-65 +-49 +3 +40 +66 +71 +-2 +-41 +-66 +-50 +2 +39 +65 +70 +-2 +-42 +-67 +-51 +1 +38 +64 +69 +-3 +-43 +-67 +-51 +1 +38 +63 +80 +80 +2 +-40 +-68 +-86 +-64 +-9 +31 +60 +78 +80 +3 +-39 +-66 +-84 +-62 +-7 +32 +61 +79 +81 +4 +-38 +-65 +-83 +-61 +-5 +34 +62 +80 +82 +5 +-37 +-65 +-82 +-60 +-5 +35 +63 +81 +83 +6 +-36 +-64 +-82 +-60 +-5 +35 +63 +71 +0 +-38 +-63 +-47 +5 +42 +67 +73 +0 +-39 +-65 +-48 +3 +40 +66 +71 +-2 +-41 +-66 +-50 +2 +39 +65 +70 +-2 +-41 +-67 +-50 +2 +38 +64 +70 +-2 +-41 +-67 +-50 +1 +38 +64 +70 +-3 +-42 +-67 +-50 +1 +38 +64 +70 +-3 +-42 +-67 +-83 +-60 +-5 +36 +64 +83 +84 +8 +-35 +-62 +-81 +-58 +-3 +36 +64 +82 +83 +7 +-36 +-64 +-82 +-60 +-5 +35 +62 +82 +82 +6 +-37 +-65 +-82 +-60 +-5 +35 +62 +81 +82 +5 +-37 +-65 +-83 +-60 +-6 +34 +62 +81 +82 +6 +-37 +-65 +-50 +1 +37 +62 +68 +-5 +-44 +-68 +-52 +0 +37 +63 +69 +-3 +-42 +-67 +-51 +2 +39 +64 +71 +-2 +-41 +-66 +-50 +2 +39 +65 +71 +-2 +-41 +-65 +-49 +3 +40 +65 +72 +-1 +-40 +-65 +-49 +2 +39 +65 +71 +-2 +-42 +-66 +-82 +-59 +-3 +37 +65 +84 +85 +9 +-34 +-62 +-80 +-58 +-4 +36 +64 +83 +84 +7 +-35 +-63 +-81 +-59 +-4 +36 +63 +82 +83 +6 +-36 +-64 +-82 +-60 +-5 +35 +62 +81 +82 +5 +-37 +-65 +-83 +-60 +-6 +34 +62 +80 +82 +5 +-38 +-65 +-51 +0 +36 +61 +67 +-6 +-45 +-70 +-53 +-1 +36 +62 +68 +-4 +-43 +-67 +-51 +0 +38 +63 +69 +-3 +-43 +-67 +-51 +1 +38 +64 +70 +-3 +-42 +-67 +-51 +1 +38 +63 +69 +-3 +-43 +-67 +-51 +1 +38 +64 +70 +-3 +-43 +-67 +-51 +1 +38 +63 +80 +80 +3 +-40 +-68 +-85 +-64 +-8 +32 +60 +78 +80 +3 +-39 +-67 +-85 +-63 +-7 +32 +60 +79 +81 +4 +-39 +-66 +-84 +-62 +-6 +33 +61 +80 +81 +5 +-38 +-66 +-83 +-61 +-5 +34 +63 +81 +83 +6 +-36 +-64 +-81 +-60 +-4 +35 +63 +81 +83 +6 +-36 +-64 +-82 +-60 +-5 +34 +62 +80 +82 +5 +-37 +-65 +-83 +-61 +-6 +33 +61 +80 +81 +5 +-38 +-66 +-83 +-61 +-6 +34 +62 +80 +82 +5 +-37 +-65 +-82 +-61 +-6 +34 +62 +80 +82 +5 +-37 +-65 +-83 +-61 +-6 +33 +62 +69 +-2 +-40 +-65 +-48 +3 +40 +66 +72 +-1 +-40 +-65 +-49 +3 +40 +66 +71 +-1 +-40 +-66 +-49 +3 +39 +65 +71 +-2 +-41 +-66 +-50 +2 +39 +65 +70 +-3 +-42 +-67 +-50 +1 +38 +64 +70 +-3 +-42 +-67 +-50 +1 +38 +64 +80 +81 +4 +-39 +-67 +-84 +-63 +-7 +32 +61 +79 +81 +5 +-38 +-66 +-83 +-62 +-6 +34 +62 +80 +82 +5 +-37 +-65 +-82 +-61 +-6 +34 +62 +81 +82 +5 +-37 +-65 +-83 +-61 +-5 +34 +63 +81 +83 +6 +-36 +-64 +-81 +-60 +-4 +35 +63 +71 +0 +-39 +-63 +-47 +5 +42 +67 +73 +0 +-39 +-64 +-48 +4 +41 +67 +73 +0 +-39 +-64 +-48 +4 +40 +66 +72 +-1 +-40 +-65 +-49 +3 +39 +65 +71 +-2 +-41 +-67 +-50 +2 +38 +64 +70 +-3 +-42 +-68 +-51 +1 +38 +64 +70 +-3 +-42 +-67 +-50 +1 +38 +64 +70 +-3 +-42 +-68 +-51 +1 +38 +64 +69 +-3 +-43 +-68 +-51 +1 +38 +64 +70 +-3 +-42 +-67 +-51 +1 +38 +64 +70 +-3 +-42 +-67 +-51 +1 +38 +64 +70 +-3 +-42 +-68 +-51 +1 +38 +64 +69 +-3 +-42 +-68 +-84 +-61 +-4 +36 +64 +83 +84 +8 +-34 +-62 +-80 +-58 +-3 +37 +64 +83 +84 +7 +-35 +-63 +-81 +-59 +-4 +36 +64 +82 +83 +7 +-36 +-63 +-82 +-59 +-4 +35 +63 +82 +83 +6 +-36 +-64 +-82 +-60 +-5 +35 +62 +82 +82 +6 +-37 +-65 +-82 +-59 +-5 +35 +63 +82 +83 +6 +-37 +-64 +-82 +-60 +-5 +35 +63 +81 +83 +7 +-37 +-64 +-82 +-60 +-5 +34 +62 +81 +82 +6 +-37 +-64 +-83 +-61 +-5 +34 +62 +81 +81 +5 +-37 +-65 +-83 +-61 +-6 +34 +61 +80 +81 +5 +-38 +-66 +-51 +0 +35 +61 +66 +-6 +-45 +-70 +-54 +-2 +35 +61 +67 +-5 +-44 +-69 +-53 +-1 +36 +62 +68 +-4 +-43 +-68 +-52 +0 +37 +63 +69 +-3 +-42 +-68 +-51 +1 +38 +64 +70 +-3 +-42 +-66 +-50 +2 +39 +65 +71 +-2 +-41 +-66 +-50 +2 +39 +65 +81 +81 +4 +-39 +-66 +-84 +-63 +-7 +32 +60 +79 +80 +4 +-38 +-66 +-84 +-62 +-6 +33 +61 +80 +82 +4 +-38 +-65 +-83 +-61 +-6 +33 +62 +80 +82 +5 +-38 +-65 +-83 +-62 +-6 +33 +61 +80 +81 +5 +-37 +-65 +-83 +-61 +-6 +34 +61 +70 +-2 +-41 +-65 +-48 +4 +41 +66 +72 +-1 +-40 +-65 +-49 +3 +40 +65 +71 +-2 +-41 +-66 +-50 +2 +39 +64 +71 +-2 +-42 +-66 +-50 +2 +39 +64 +70 +-2 +-42 +-66 +-50 +2 +39 +64 +70 +-3 +-42 +-67 +-50 +2 +39 +64 +70 +-2 +-42 +-67 +-50 +2 +39 +64 +70 +-2 +-41 +-66 +-50 +2 +39 +64 +70 +-2 +-41 +-66 +-50 +2 +39 +64 +71 +-2 +-41 +-66 +-50 +2 +39 +65 +71 +-2 +-42 +-66 +-50 +2 +39 +64 +71 +-2 +-42 +-66 +-50 +2 +39 +65 +71 +-2 +-41 +-66 +-82 +-59 +-3 +36 +64 +83 +84 +8 +-35 +-63 +-81 +-59 +-4 +36 +63 +82 +83 +7 +-36 +-63 +-81 +-59 +-5 +35 +62 +81 +83 +6 +-37 +-64 +-83 +-60 +-5 +34 +62 +81 +82 +6 +-37 +-64 +-82 +-60 +-5 +34 +62 +81 +82 +6 +-37 +-64 +-83 +-61 +-6 +34 +62 +81 +82 +6 +-37 +-65 +-82 +-60 +-6 +34 +62 +81 +82 +5 +-38 +-65 +-83 +-60 +-6 +34 +62 +80 +82 +6 +-37 +-64 +-83 +-60 +-5 +34 +63 +82 +83 +7 +-37 +-64 +-81 +-60 +-5 +35 +63 +82 +83 +7 +-36 +-63 +-49 +2 +37 +63 +69 +-4 +-43 +-69 +-53 +0 +37 +63 +69 +-4 +-43 +-68 +-51 +1 +37 +64 +70 +-3 +-42 +-67 +-51 +1 +38 +64 +70 +-3 +-42 +-68 +-51 +1 +37 +63 +69 +-4 +-43 +-68 +-52 +0 +37 +63 +69 +-4 +-43 +-68 +-52 +1 +37 +63 +69 +-4 +-43 +-68 +-51 +1 +37 +63 +69 +-3 +-42 +-67 +-50 +1 +38 +64 +70 +-3 +-42 +-67 +-51 +1 +38 +64 +70 +-3 +-42 +-67 +-51 +2 +38 +64 +70 +-3 +-41 +-66 +-50 +2 +39 +65 +71 +-2 +-41 +-66 +-50 +2 +39 +65 +81 +81 +4 +-39 +-66 +-84 +-63 +-7 +32 +60 +79 +80 +4 +-38 +-66 +-84 +-62 +-6 +33 +61 +79 +81 +4 +-38 +-66 +-84 +-62 +-6 +33 +61 +79 +82 +5 +-38 +-65 +-83 +-61 +-6 +33 +61 +80 +81 +5 +-38 +-66 +-83 +-62 +-6 +33 +61 +80 +82 +5 +-37 +-65 +-83 +-61 +-6 +34 +62 +80 +82 +5 +-37 +-65 +-83 +-61 +-6 +33 +62 +80 +82 +5 +-38 +-65 +-83 +-61 +-6 +33 +61 +80 +82 +5 +-37 +-65 +-82 +-60 +-5 +34 +62 +81 +82 +5 +-36 +-64 +-82 +-60 +-5 +34 +63 +71 +-1 +-39 +-63 +-47 +5 +42 +68 +73 +1 +-39 +-64 +-47 +4 +41 +67 +73 +0 +-39 +-65 +-48 +3 +40 +66 +72 +-1 +-40 +-65 +-49 +3 +40 +65 +71 +-1 +-41 +-66 +-50 +2 +39 +65 +71 +-2 +-41 +-66 +-50 +2 +39 +64 +71 +-2 +-41 +-66 +-50 +2 +39 +65 +71 +-2 +-42 +-67 +-51 +1 +39 +65 +72 +-2 +-42 +-68 +-52 +0 +38 +65 +71 +-1 +-40 +-66 +-52 +0 +37 +64 +71 +-2 +-41 +-67 +-51 +1 +37 +63 +69 +-3 +-42 +-67 +-51 +1 +37 +63 +69 +-4 +-44 +-68 +-85 +-61 +-5 +36 +64 +82 +82 +6 +-37 +-64 +-82 +-60 +-5 +34 +63 +82 +83 +6 +-37 +-64 +-82 +-61 +-6 +34 +63 +83 +83 +7 +-37 +-65 +-84 +-61 +-5 +36 +64 +83 +83 +6 +-38 +-66 +-83 +-61 +-5 +36 +64 +82 +82 +5 +-38 +-64 +-50 +1 +38 +62 +68 +-4 +-44 +-69 +-53 +-1 +36 +62 +69 +-4 +-43 +-68 +-52 +1 +38 +63 +69 +-3 +-42 +-67 +-51 +2 +39 +64 +70 +-2 +-41 +-66 +-50 +2 +39 +64 +70 +-2 +-42 +-66 +-50 +2 +39 +64 +70 +-3 +-42 +-66 +-83 +-60 +-4 +36 +64 +83 +84 +8 +-35 +-63 +-81 +-59 +-4 +36 +63 +82 +84 +7 +-36 +-63 +-82 +-59 +-4 +36 +63 +82 +83 +7 +-36 +-63 +-82 +-60 +-5 +35 +63 +81 +83 +7 +-37 +-64 +-82 +-60 +-5 +35 +63 +82 +83 +6 +-37 +-64 +-82 +-60 +-5 +35 +63 +82 +83 +6 +-37 +-64 +-82 +-60 +-5 +35 +62 +81 +82 +5 +-37 +-64 +-83 +-60 +-6 +35 +62 +81 +83 +6 +-37 +-64 +-82 +-60 +-4 +35 +63 +82 +83 +7 +-36 +-64 +-82 +-60 +-4 +36 +63 +82 +83 +6 +-36 +-64 +-49 +2 +37 +63 +68 +-5 +-44 +-69 +-53 +-1 +36 +62 +68 +-5 +-43 +-68 +-52 +0 +37 +63 +69 +-4 +-43 +-68 +-51 +1 +37 +63 +69 +-4 +-42 +-68 +-51 +1 +37 +64 +70 +-3 +-42 +-67 +-51 +2 +38 +64 +70 +-3 +-42 +-67 +-51 +2 +38 +64 +80 +81 +3 +-39 +-67 +-85 +-64 +-8 +31 +60 +78 +80 +3 +-39 +-67 +-85 +-63 +-7 +32 +60 +79 +81 +4 +-38 +-66 +-84 +-62 +-7 +33 +61 +79 +82 +4 +-38 +-66 +-83 +-61 +-6 +34 +62 +80 +82 +6 +-37 +-65 +-82 +-61 +-5 +34 +62 +70 +-1 +-39 +-64 +-48 +4 +41 +66 +72 +0 +-39 +-64 +-48 +4 +40 +66 +72 +-1 +-40 +-65 +-49 +3 +39 +65 +71 +-2 +-41 +-66 +-50 +2 +39 +65 +71 +-2 +-41 +-66 +-50 +2 +39 +65 +70 +-2 +-41 +-67 +-50 +2 +38 +64 +70 +-3 +-42 +-67 +-51 +1 +37 +64 +69 +-3 +-42 +-68 +-51 +1 +38 +64 +69 +-3 +-42 +-67 +-51 +1 +38 +64 +70 +-3 +-42 +-67 +-51 +1 +38 +64 +70 +-3 +-42 +-67 +-50 +1 +38 +64 +70 +-3 +-42 +-67 +-50 +2 +38 +64 +70 +-3 +-42 +-67 +-83 +-60 +-4 +37 +64 +83 +84 +8 +-34 +-62 +-81 +-59 +-4 +36 +63 +82 +83 +7 +-37 +-64 +-82 +-60 +-5 +35 +63 +82 +83 +6 +-36 +-64 +-82 +-60 +-5 +35 +62 +81 +82 +6 +-37 +-65 +-83 +-61 +-6 +34 +62 +81 +82 +6 +-37 +-64 +-49 +1 +38 +63 +69 +-4 +-43 +-68 +-52 +0 +38 +64 +70 +-3 +-44 +-69 +-52 +0 +38 +64 +71 +-2 +-42 +-68 +-52 +-1 +37 +64 +71 +-1 +-41 +-67 +-51 +0 +37 +64 +71 +-1 +-41 +-66 +-50 +2 +39 +64 +70 +-2 +-42 +-67 +-82 +-59 +-3 +38 +66 +84 +84 +7 +-36 +-63 +-81 +-59 +-4 +36 +64 +83 +84 +7 +-36 +-63 +-82 +-61 +-6 +35 +64 +83 +84 +7 +-36 +-65 +-83 +-61 +-5 +36 +65 +84 +84 +7 +-37 +-65 +-82 +-60 +-4 +37 +64 +83 +83 +6 +-37 +-64 +-49 +2 +38 +63 +69 +-4 +-43 +-68 +-52 +0 +37 +63 +69 +-4 +-43 +-68 +-51 +0 +37 +63 +69 +-3 +-43 +-68 +-51 +1 +37 +63 +69 +-3 +-43 +-68 +-51 +1 +38 +64 +70 +-3 +-42 +-67 +-50 +2 +38 +64 +70 +-3 +-42 +-67 +-51 +2 +38 +64 +80 +81 +3 +-40 +-68 +-85 +-63 +-8 +32 +60 +79 +80 +4 +-38 +-66 +-84 +-62 +-6 +33 +61 +80 +82 +5 +-37 +-65 +-83 +-61 +-6 +34 +62 +80 +82 +5 +-37 +-65 +-83 +-61 +-5 +34 +62 +81 +82 +5 +-37 +-65 +-82 +-61 +-5 +34 +62 +80 +81 +5 +-37 +-66 +-83 +-61 +-6 +33 +61 +80 +82 +5 +-37 +-65 +-83 +-61 +-5 +34 +62 +80 +82 +5 +-37 +-65 +-82 +-60 +-5 +34 +62 +81 +83 +6 +-36 +-65 +-82 +-60 +-5 +35 +62 +81 +83 +5 +-37 +-65 +-82 +-60 +-5 +34 +62 +70 +-1 +-40 +-64 +-48 +4 +41 +66 +72 +0 +-40 +-65 +-49 +3 +40 +66 +71 +-2 +-41 +-66 +-50 +2 +39 +65 +70 +-2 +-41 +-66 +-49 +3 +39 +65 +71 +-2 +-41 +-66 +-49 +2 +39 +65 +71 +-2 +-41 +-66 +-50 +2 +39 +65 +71 +-2 +-41 +-66 +-49 +2 +39 +65 +71 +-2 +-41 +-66 +-49 +2 +39 +65 +70 +-2 +-41 +-66 +-50 +1 +39 +64 +70 +-2 +-41 +-67 +-50 +1 +38 +64 +70 +-2 +-42 +-67 +-50 +1 +38 +64 +70 +-3 +-42 +-67 +-50 +2 +38 +64 +70 +-3 +-42 +-67 +-83 +-60 +-4 +37 +64 +83 +85 +8 +-34 +-62 +-80 +-58 +-4 +36 +64 +82 +83 +7 +-37 +-64 +-82 +-60 +-5 +34 +62 +81 +82 +6 +-37 +-65 +-83 +-61 +-6 +34 +61 +80 +81 +5 +-37 +-65 +-83 +-61 +-5 +35 +62 +81 +82 +6 +-37 +-65 +-49 +2 +37 +63 +69 +-4 +-43 +-68 +-52 +0 +37 +63 +69 +-3 +-42 +-68 +-51 +1 +38 +64 +70 +-3 +-41 +-66 +-50 +2 +39 +65 +71 +-2 +-41 +-66 +-50 +2 +38 +64 +70 +-2 +-41 +-67 +-50 +2 +38 +64 +70 +-3 +-42 +-68 +-83 +-60 +-5 +36 +64 +83 +84 +8 +-35 +-63 +-81 +-58 +-4 +36 +64 +82 +84 +7 +-36 +-63 +-82 +-59 +-5 +35 +63 +81 +83 +6 +-37 +-64 +-82 +-60 +-5 +34 +62 +81 +82 +6 +-37 +-64 +-82 +-60 +-5 +35 +63 +82 +83 +7 +-36 +-63 +-49 +2 +38 +63 +69 +-5 +-43 +-68 +-52 +0 +37 +63 +69 +-4 +-42 +-67 +-51 +1 +38 +64 +70 +-3 +-42 +-66 +-51 +2 +38 +64 +70 +-3 +-42 +-67 +-51 +1 +38 +64 +70 +-3 +-42 +-67 +-51 +1 +38 +64 +70 +-3 +-42 +-67 +-51 +1 +38 +63 +80 +80 +3 +-40 +-68 +-85 +-64 +-8 +31 +59 +78 +80 +3 +-39 +-67 +-84 +-62 +-7 +33 +61 +79 +82 +5 +-38 +-65 +-83 +-61 +-5 +34 +62 +81 +82 +6 +-37 +-65 +-82 +-60 +-5 +35 +63 +81 +83 +6 +-36 +-64 +-81 +-60 +-5 +35 +63 +71 +-1 +-39 +-64 +-48 +4 +41 +66 +72 +0 +-40 +-65 +-49 +3 +39 +65 +71 +-2 +-42 +-67 +-51 +1 +38 +63 +69 +-4 +-43 +-68 +-52 +0 +37 +63 +69 +-4 +-43 +-68 +-52 +1 +38 +63 +69 +-4 +-43 +-67 +-51 +1 +38 +63 +80 +80 +3 +-39 +-68 +-85 +-63 +-8 +32 +60 +79 +80 +3 +-38 +-66 +-84 +-61 +-6 +34 +62 +80 +82 +5 +-37 +-64 +-83 +-61 +-5 +34 +63 +81 +83 +6 +-37 +-64 +-82 +-60 +-5 +34 +62 +81 +82 +5 +-37 +-65 +-83 +-61 +-6 +33 +61 +69 +-2 +-40 +-65 +-48 +3 +40 +66 +72 +-1 +-41 +-66 +-49 +2 +39 +65 +71 +-2 +-41 +-66 +-50 +2 +39 +65 +71 +-2 +-41 +-66 +-49 +3 +39 +65 +71 +-2 +-41 +-66 +-49 +3 +39 +65 +71 +-2 +-41 +-66 +-50 +2 +39 +65 +71 +-2 +-41 +-66 +-49 +2 +39 +65 +71 +-2 +-42 +-66 +-50 +2 +39 +64 +70 +-2 +-42 +-66 +-50 +2 +38 +64 +70 +-3 +-42 +-67 +-51 +1 +38 +64 +70 +-3 +-42 +-67 +-51 +1 +38 +63 +70 +-3 +-43 +-67 +-51 +1 +38 +63 +69 +-3 +-43 +-68 +-52 +0 +37 +63 +69 +-4 +-43 +-67 +-51 +1 +37 +63 +69 +-4 +-43 +-68 +-52 +0 +37 +62 +69 +-4 +-43 +-68 +-52 +0 +37 +63 +69 +-4 +-43 +-67 +-51 +1 +38 +63 +70 +-3 +-42 +-67 +-51 +2 +38 +64 +70 +-3 +-42 +-66 +-50 +2 +39 +64 +81 +81 +4 +-38 +-66 +-84 +-62 +-7 +32 +61 +79 +81 +4 +-38 +-66 +-84 +-62 +-6 +33 +61 +80 +82 +5 +-38 +-65 +-83 +-61 +-5 +34 +62 +81 +82 +6 +-36 +-64 +-82 +-60 +-5 +35 +63 +81 +83 +6 +-37 +-64 +-82 +-60 +-5 +35 +63 +81 +83 +6 +-36 +-64 +-82 +-60 +-5 +35 +63 +81 +83 +6 +-37 +-64 +-82 +-60 +-5 +34 +63 +81 +83 +6 +-37 +-65 +-82 +-61 +-5 +34 +63 +81 +82 +5 +-37 +-65 +-83 +-61 +-5 +34 +62 +81 +82 +5 +-37 +-65 +-83 +-60 +-5 +34 +62 +81 +82 +5 +-37 +-65 +-83 +-61 +-5 +34 +62 +80 +82 +5 +-37 +-65 +-82 +-61 +-5 +33 +62 +80 +82 +5 +-37 +-65 +-82 +-61 +-6 +34 +61 +80 +82 +5 +-37 +-65 +-83 +-61 +-5 +34 +62 +80 +82 +5 +-37 +-65 +-83 +-61 +-5 +34 +63 +71 +-1 +-39 +-64 +-47 +5 +42 +68 +73 +1 +-38 +-64 +-47 +4 +41 +67 +72 +0 +-39 +-65 +-48 +3 +40 +66 +72 +-1 +-40 +-65 +-49 +3 +39 +65 +71 +-2 +-41 +-67 +-50 +2 +38 +64 +70 +-3 +-42 +-67 +-50 +2 +38 +64 +80 +81 +3 +-39 +-67 +-85 +-64 +-8 +31 +60 +78 +80 +3 +-39 +-67 +-85 +-63 +-7 +32 +60 +79 +81 +4 +-38 +-66 +-83 +-61 +-6 +33 +62 +80 +82 +5 +-37 +-64 +-82 +-60 +-4 +35 +63 +82 +84 +7 +-36 +-63 +-81 +-59 +-4 +35 +63 +82 +83 +6 +-36 +-64 +-81 +-60 +-5 +34 +62 +81 +82 +5 +-37 +-65 +-82 +-60 +-5 +34 +62 +81 +82 +5 +-37 +-65 +-83 +-61 +-5 +33 +62 +80 +81 +5 +-38 +-66 +-84 +-63 +-7 +32 +60 +79 +81 +4 +-38 +-67 +-84 +-62 +-7 +33 +61 +69 +-2 +-41 +-66 +-49 +3 +40 +66 +72 +-1 +-40 +-65 +-49 +3 +40 +66 +72 +-1 +-40 +-65 +-49 +3 +40 +66 +72 +-1 +-40 +-65 +-49 +3 +40 +65 +71 +-1 +-41 +-65 +-49 +2 +39 +65 +71 +-2 +-41 +-65 +-49 +2 +40 +65 +71 +-1 +-41 +-65 +-49 +2 +40 +65 +71 +-2 +-41 +-66 +-50 +2 +39 +65 +71 +-2 +-42 +-66 +-50 +2 +39 +64 +70 +-2 +-42 +-67 +-51 +2 +38 +64 +70 +-3 +-42 +-67 +-51 +2 +38 +64 +70 +-3 +-42 +-67 +-51 +2 +39 +64 +70 +-3 +-42 +-67 +-83 +-59 +-4 +36 +65 +84 +85 +9 +-34 +-62 +-80 +-58 +-3 +37 +64 +83 +84 +7 +-35 +-63 +-81 +-59 +-4 +36 +63 +82 +83 +7 +-36 +-63 +-81 +-59 +-5 +36 +64 +82 +84 +7 +-36 +-63 +-81 +-59 +-4 +36 +63 +82 +83 +7 +-36 +-63 +-49 +1 +38 +63 +68 +-5 +-44 +-69 +-53 +-1 +36 +62 +68 +-4 +-44 +-68 +-52 +0 +37 +62 +69 +-4 +-44 +-68 +-52 +0 +37 +63 +69 +-4 +-43 +-67 +-51 +0 +37 +63 +69 +-4 +-43 +-67 +-51 +1 +38 +64 +70 +-3 +-42 +-67 +-51 +1 +38 +63 +80 +80 +3 +-40 +-68 +-85 +-63 +-8 +32 +60 +78 +81 +4 +-38 +-66 +-84 +-62 +-6 +33 +62 +80 +82 +5 +-37 +-65 +-82 +-60 +-5 +34 +63 +81 +83 +6 +-36 +-64 +-81 +-60 +-4 +35 +62 +81 +83 +6 +-36 +-65 +-82 +-61 +-5 +34 +63 +70 +-1 +-39 +-64 +-47 +4 +41 +66 +72 +-1 +-41 +-66 +-49 +2 +39 +65 +71 +-2 +-41 +-67 +-50 +1 +38 +64 +70 +-3 +-42 +-67 +-50 +1 +38 +64 +70 +-3 +-42 +-67 +-50 +1 +38 +64 +70 +-2 +-42 +-66 +-50 +2 +39 +65 +81 +82 +4 +-39 +-67 +-85 +-63 +-7 +33 +61 +79 +82 +5 +-38 +-66 +-83 +-61 +-6 +33 +62 +80 +82 +5 +-37 +-65 +-82 +-61 +-5 +34 +62 +80 +82 +5 +-37 +-65 +-83 +-61 +-6 +34 +62 +80 +82 +5 +-37 +-64 +-82 +-60 +-5 +34 +62 +70 +-1 +-39 +-63 +-47 +5 +41 +67 +72 +-1 +-40 +-65 +-49 +3 +40 +66 +71 +-2 +-41 +-66 +-50 +2 +39 +64 +70 +-3 +-41 +-67 +-51 +2 +38 +64 +70 +-3 +-42 +-67 +-51 +1 +38 +64 +70 +-3 +-42 +-67 +-51 +2 +38 +64 +70 +-3 +-42 +-67 +-83 +-60 +-4 +36 +64 +83 +84 +8 +-35 +-62 +-81 +-59 +-3 +36 +64 +83 +83 +7 +-36 +-63 +-81 +-60 +-5 +35 +62 +82 +83 +6 +-36 +-64 +-82 +-60 +-5 +35 +63 +82 +83 +7 +-36 +-63 +-81 +-59 +-4 +36 +64 +82 +83 +7 +-36 +-63 +-49 +2 +38 +63 +69 +-4 +-44 +-68 +-52 +0 +37 +62 +69 +-4 +-43 +-67 +-51 +1 +38 +63 +69 +-4 +-43 +-68 +-52 +1 +37 +63 +69 +-3 +-43 +-68 +-51 +1 +37 +63 +69 +-3 +-42 +-67 +-51 +1 +38 +63 +69 +-3 +-43 +-68 +-84 +-61 +-5 +35 +64 +83 +84 +7 +-36 +-63 +-81 +-59 +-4 +36 +63 +82 +83 +7 +-36 +-64 +-82 +-60 +-5 +35 +62 +82 +83 +6 +-36 +-64 +-82 +-60 +-5 +35 +63 +81 +83 +6 +-37 +-64 +-82 +-60 +-5 +34 +62 +81 +82 +6 +-37 +-64 +-50 +1 +37 +62 +68 +-5 +-44 +-68 +-52 +-1 +37 +62 +68 +-4 +-44 +-68 +-51 +0 +37 +63 +69 +-3 +-42 +-67 +-51 +1 +38 +64 +69 +-3 +-42 +-67 +-51 +1 +38 +64 +70 +-3 +-42 +-67 +-51 +1 +38 +64 +70 +-3 +-42 +-67 +-51 +1 +38 +64 +80 +81 +3 +-39 +-67 +-85 +-63 +-7 +32 +60 +79 +80 +4 +-38 +-66 +-83 +-62 +-6 +33 +61 +80 +82 +5 +-37 +-65 +-83 +-61 +-5 +34 +62 +81 +83 +6 +-36 +-64 +-82 +-60 +-4 +35 +63 +81 +83 +6 +-37 +-64 +-82 +-60 +-5 +34 +63 +71 +-1 +-39 +-64 +-47 +4 +40 +66 +72 +-1 +-40 +-65 +-49 +3 +39 +66 +72 +-2 +-41 +-66 +-50 +3 +39 +65 +71 +-2 +-41 +-66 +-49 +3 +39 +65 +71 +-2 +-41 +-66 +-49 +2 +39 +65 +71 +-2 +-41 +-66 +-50 +2 +39 +65 +81 +81 +4 +-39 +-67 +-84 +-62 +-7 +32 +61 +79 +81 +4 +-38 +-66 +-83 +-62 +-6 +33 +61 +80 +82 +5 +-37 +-66 +-83 +-61 +-6 +33 +61 +79 +81 +4 +-38 +-66 +-84 +-62 +-7 +33 +61 +79 +81 +4 +-39 +-66 +-84 +-63 +-7 +32 +61 +80 +81 +4 +-38 +-66 +-84 +-62 +-7 +33 +61 +79 +81 +4 +-38 +-66 +-83 +-61 +-6 +34 +62 +80 +82 +5 +-37 +-65 +-83 +-61 +-5 +34 +62 +80 +82 +5 +-37 +-64 +-82 +-61 +-5 +34 +62 +81 +82 +5 +-37 +-65 +-82 +-60 +-5 +34 +62 +70 +-1 +-40 +-64 +-48 +4 +41 +66 +72 +0 +-40 +-65 +-49 +3 +40 +65 +72 +-1 +-41 +-65 +-49 +3 +40 +65 +71 +-1 +-40 +-65 +-49 +3 +40 +65 +72 +-1 +-40 +-65 +-49 +3 +40 +66 +72 +-1 +-40 +-65 +-49 +3 +40 +66 +71 +-1 +-41 +-65 +-49 +3 +39 +65 +71 +-2 +-41 +-65 +-49 +2 +39 +65 +71 +-2 +-42 +-67 +-50 +1 +38 +64 +70 +-2 +-42 +-66 +-50 +2 +39 +64 +70 +-2 +-42 +-67 +-50 +2 +39 +64 +70 +-2 +-42 +-67 +-50 +1 +38 +64 +70 +-3 +-42 +-67 +-83 +-60 +-4 +36 +64 +83 +84 +8 +-35 +-62 +-80 +-59 +-4 +36 +64 +83 +84 +7 +-35 +-63 +-81 +-59 +-4 +36 +63 +82 +83 +6 +-37 +-64 +-82 +-60 +-5 +35 +63 +81 +82 +6 +-37 +-64 +-82 +-61 +-5 +34 +62 +81 +82 +5 +-38 +-65 +-51 +0 +35 +61 +67 +-6 +-45 +-70 +-54 +-2 +35 +61 +67 +-5 +-44 +-70 +-53 +-1 +36 +62 +68 +-5 +-44 +-69 +-53 +0 +36 +62 +68 +-4 +-43 +-68 +-51 +1 +37 +63 +69 +-3 +-42 +-67 +-51 +1 +38 +64 +70 +-3 +-42 +-66 +-50 +2 +39 +65 +81 +81 +4 +-39 +-67 +-85 +-63 +-8 +32 +60 +78 +80 +3 +-39 +-67 +-84 +-62 +-7 +33 +61 +79 +81 +5 +-38 +-66 +-83 +-61 +-6 +33 +62 +80 +82 +5 +-37 +-65 +-82 +-61 +-5 +34 +62 +81 +83 +5 +-37 +-65 +-82 +-60 +-5 +34 +62 +70 +-1 +-39 +-64 +-47 +4 +41 +67 +72 +0 +-40 +-65 +-48 +3 +40 +66 +71 +-1 +-41 +-66 +-50 +2 +39 +65 +71 +-2 +-41 +-66 +-50 +2 +39 +64 +70 +-3 +-42 +-67 +-50 +2 +38 +64 +70 +-3 +-42 +-67 +-51 +1 +37 +63 +80 +80 +3 +-40 +-68 +-85 +-64 +-8 +31 +59 +78 +79 +3 +-39 +-67 +-85 +-63 +-7 +33 +61 +79 +81 +4 +-37 +-66 +-83 +-61 +-6 +34 +62 +81 +83 +6 +-37 +-64 +-82 +-60 +-4 +35 +63 +81 +83 +6 +-36 +-64 +-81 +-60 +-4 +35 +63 +71 +0 +-38 +-63 +-47 +5 +41 +67 +73 +0 +-39 +-65 +-48 +4 +40 +66 +72 +-1 +-40 +-65 +-49 +3 +39 +65 +71 +-2 +-41 +-67 +-50 +2 +38 +64 +70 +-3 +-42 +-68 +-51 +1 +37 +64 +69 +-3 +-42 +-68 +-51 +1 +37 +63 +69 +-3 +-42 +-67 +-83 +-60 +-4 +36 +64 +83 +85 +9 +-35 +-61 +-79 +-58 +-2 +37 +65 +84 +84 +8 +-35 +-63 +-80 +-59 +-4 +36 +64 +83 +84 +8 +-35 +-63 +-80 +-58 +-4 +36 +64 +82 +84 +7 +-36 +-63 +-81 +-59 +-4 +36 +63 +82 +83 +6 +-37 +-64 +-49 +2 +38 +62 +69 +-4 +-44 +-69 +-53 +-1 +37 +62 +68 +-5 +-44 +-68 +-52 +0 +37 +63 +69 +-4 +-43 +-68 +-52 +0 +37 +62 +69 +-4 +-43 +-68 +-52 +0 +37 +63 +69 +-4 +-43 +-68 +-52 +0 +37 +63 +69 +-4 +-43 +-68 +-84 +-61 +-5 +35 +64 +83 +83 +7 +-35 +-63 +-81 +-59 +-4 +36 +63 +82 +83 +6 +-36 +-63 +-81 +-59 +-5 +35 +63 +81 +83 +6 +-37 +-64 +-82 +-60 +-5 +35 +63 +82 +83 +7 +-37 +-64 +-82 +-60 +-5 +35 +63 +82 +82 +6 +-37 +-64 +-49 +1 +37 +63 +68 +-5 +-44 +-69 +-53 +-1 +36 +62 +68 +-4 +-43 +-68 +-52 +0 +37 +63 +69 +-4 +-43 +-68 +-51 +1 +37 +63 +69 +-3 +-42 +-68 +-51 +1 +37 +64 +69 +-3 +-42 +-67 +-50 +2 +38 +64 +70 +-2 +-41 +-66 +-50 +2 +39 +65 +81 +81 +4 +-39 +-67 +-84 +-63 +-7 +32 +61 +79 +81 +4 +-38 +-66 +-83 +-61 +-6 +34 +62 +80 +82 +5 +-37 +-65 +-83 +-61 +-5 +34 +62 +80 +82 +5 +-37 +-65 +-83 +-61 +-5 +34 +62 +81 +82 +6 +-36 +-65 +-82 +-61 +-5 +35 +62 +71 +-1 +-39 +-64 +-48 +4 +41 +66 +72 +-1 +-40 +-65 +-49 +3 +40 +65 +71 +-2 +-41 +-66 +-50 +2 +39 +65 +71 +-2 +-41 +-65 +-49 +3 +39 +65 +71 +-2 +-41 +-66 +-50 +2 +39 +64 +71 +-2 +-42 +-67 +-50 +2 +39 +64 +80 +81 +3 +-39 +-68 +-85 +-64 +-8 +31 +59 +78 +80 +3 +-39 +-67 +-84 +-63 +-7 +32 +60 +79 +80 +4 +-38 +-66 +-84 +-63 +-7 +32 +60 +79 +81 +4 +-38 +-66 +-83 +-61 +-6 +34 +61 +80 +82 +5 +-37 +-65 +-82 +-60 +-5 +35 +63 +71 +0 +-38 +-62 +-46 +6 +43 +68 +74 +1 +-38 +-63 +-47 +5 +42 +67 +73 +1 +-39 +-64 +-48 +4 +41 +66 +72 +0 +-40 +-64 +-48 +4 +41 +66 +72 +-1 +-40 +-65 +-49 +3 +40 +65 +71 +-2 +-41 +-67 +-50 +1 +39 +64 +70 +-3 +-42 +-67 +-83 +-60 +-4 +36 +64 +83 +84 +7 +-35 +-63 +-81 +-59 +-5 +35 +63 +81 +83 +6 +-37 +-64 +-82 +-60 +-5 +35 +63 +82 +82 +6 +-37 +-64 +-82 +-60 +-5 +35 +63 +82 +82 +6 +-37 +-64 +-82 +-60 +-5 +35 +62 +81 +83 +6 +-37 +-64 +-50 +1 +37 +63 +68 +-4 +-43 +-69 +-52 +0 +37 +63 +69 +-4 +-43 +-68 +-51 +1 +37 +64 +69 +-3 +-42 +-67 +-50 +1 +38 +64 +70 +-3 +-42 +-67 +-50 +2 +38 +64 +70 +-3 +-42 +-66 +-50 +2 +38 +64 +70 +-3 +-42 +-67 +-83 +-59 +-4 +36 +64 +83 +84 +8 +-35 +-62 +-80 +-58 +-3 +36 +64 +82 +83 +7 +-36 +-63 +-82 +-60 +-5 +35 +63 +82 +83 +6 +-37 +-64 +-82 +-60 +-5 +35 +62 +81 +82 +5 +-37 +-65 +-83 +-61 +-6 +34 +62 +80 +82 +5 +-38 +-65 +-50 +0 +36 +61 +67 +-5 +-45 +-69 +-53 +-1 +36 +62 +68 +-5 +-44 +-69 +-52 +0 +37 +63 +69 +-4 +-43 +-68 +-51 +0 +37 +63 +69 +-4 +-43 +-68 +-51 +0 +37 +63 +69 +-4 +-43 +-68 +-51 +0 +37 +64 +69 +-3 +-42 +-68 +-51 +1 +38 +64 +80 +81 +4 From 9a7304800891a95463783df822a0a918010177af Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Mon, 12 Oct 2020 14:33:17 +0200 Subject: [PATCH 08/58] adapt text for FDXB --- tools/pm3_tests.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/pm3_tests.sh b/tools/pm3_tests.sh index 4a1293cf8..41ebe1951 100755 --- a/tools/pm3_tests.sh +++ b/tools/pm3_tests.sh @@ -367,7 +367,7 @@ while true; do "EM TAG ID : 0F0368568B"; then break; fi if ! CheckExecute slow "lf T55 fdxb_animal test" "$CLIENTBIN -c 'data load -f traces/lf_ATA5577_fdxb_animal.pm3; lf search 1'" "FDX-B ID found"; then break; fi if ! CheckExecute slow "lf T55 fdxb_animal test2" "$CLIENTBIN -c 'data load -f traces/lf_ATA5577_fdxb_animal.pm3; lf fdxb demod'" \ - "Animal ID 0999-000000112233"; then break; fi + "Animal ID 999-000000112233"; then break; fi if ! CheckExecute slow "lf T55 fdxb_extended test" "$CLIENTBIN -c 'data load -f traces/lf_ATA5577_fdxb_extended.pm3; lf search 1'" "FDX-B ID found"; then break; fi if ! CheckExecute slow "lf T55 fdxb_extended test2" "$CLIENTBIN -c 'data load -f traces/lf_ATA5577_fdxb_extended.pm3; lf fdxb demod'" \ "temperature 95.2 F / 35.1 C"; then break; fi From 244283b2a9f838bfa2bbf4b2d27f05678c75b00d Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Mon, 12 Oct 2020 16:08:10 +0200 Subject: [PATCH 09/58] update help text --- Makefile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Makefile b/Makefile index 4634a8b54..89e5b4c98 100644 --- a/Makefile +++ b/Makefile @@ -173,6 +173,9 @@ help: @echo "+ .../check - Run offline tests against specific target. See above." @echo "+ miscchecks - Detect various encoding issues in source code" @echo + @echo "+ udev - Sets udev rules on *nix" + @echo "+ accessrights - Ensure user belongs to correct group on *nix" + @echo @echo "Possible platforms: try \"make PLATFORM=\" for more info, default is PM3RDV4" @echo "To activate verbose mode, use make V=1" From acada02e379465a0cfec466a28d2b3b74bf7fb76 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Mon, 12 Oct 2020 18:35:47 +0200 Subject: [PATCH 10/58] hf 14a raw - no crc check on the iso14 select card response --- client/src/cmdhf14a.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/cmdhf14a.c b/client/src/cmdhf14a.c index eebecc747..2b65be89c 100644 --- a/client/src/cmdhf14a.c +++ b/client/src/cmdhf14a.c @@ -1362,7 +1362,7 @@ static int waitCmd(uint8_t iSelect, uint32_t timeout) { uint8_t *data = resp.data.asBytes; - if (len >= 3) { + if (iSelect == 0 && len >= 3) { bool crc = check_crc(CRC_14443_A, data, len); PrintAndLogEx(SUCCESS, "%s[%02X %02X] %s", From 7cfbdcbae9b083bf214bccba86015a5919d29bc7 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Mon, 12 Oct 2020 19:08:29 +0200 Subject: [PATCH 11/58] WIP - hf mfu countertear ... --- armsrc/appmain.c | 9 +++ armsrc/mifarecmd.c | 50 +++++++++++++-- armsrc/mifarecmd.h | 2 +- client/src/cmdhfmfu.c | 140 +++++++++++++++++++++++++++++++++++++++++- include/pm3_cmd.h | 3 + 5 files changed, 197 insertions(+), 7 deletions(-) diff --git a/armsrc/appmain.c b/armsrc/appmain.c index d4b5faf27..9aa65de03 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -1473,6 +1473,15 @@ static void PacketReceived(PacketCommandNG *packet) { MifareU_Otp_Tearoff(packet->oldarg[0], packet->oldarg[1], packet->data.asBytes); break; } + case CMD_HF_MFU_COUNTER_TEAROFF: { + struct p { + uint8_t counter; + uint32_t tearoff_time; + } PACKED; + struct p *payload = (struct p *) packet->data.asBytes; + MifareU_Counter_Tearoff(payload->counter, payload->tearoff_time); + break; + } case CMD_HF_MIFARE_STATIC_NONCE: { MifareHasStaticNonce(); break; diff --git a/armsrc/mifarecmd.c b/armsrc/mifarecmd.c index 8b1bba150..f442166f4 100644 --- a/armsrc/mifarecmd.c +++ b/armsrc/mifarecmd.c @@ -2695,9 +2695,8 @@ void Mifare_DES_Auth2(uint32_t arg0, uint8_t *datain) { // // Tear-off attack against MFU. // - Moebius et al -void MifareU_Otp_Tearoff(uint8_t arg0, uint32_t arg1, uint8_t *datain) { +void MifareU_Otp_Tearoff(uint8_t arg0, uint32_t tearoff_time, uint8_t *datain) { uint8_t blockNo = arg0; - uint32_t tearOffTime = arg1; uint8_t data_fullwrite[4] = {0x00}; uint8_t data_testwrite[4] = {0x00}; memcpy(data_fullwrite, datain, 4); @@ -2705,8 +2704,8 @@ void MifareU_Otp_Tearoff(uint8_t arg0, uint32_t arg1, uint8_t *datain) { if (DBGLEVEL >= DBG_DEBUG) DbpString("Preparing OTP tear-off"); - if (tearOffTime > 43000) - tearOffTime = 43000; + if (tearoff_time > 43000) + tearoff_time = 43000; MifareUWriteBlock(blockNo, 0, data_fullwrite); @@ -2732,9 +2731,50 @@ void MifareU_Otp_Tearoff(uint8_t arg0, uint32_t arg1, uint8_t *datain) { // Wait before cutting power. aka tear-off LED_D_ON(); - SpinDelayUsPrecision(tearOffTime); + SpinDelayUsPrecision(tearoff_time); if (DBGLEVEL >= DBG_DEBUG) Dbprintf(_YELLOW_("OTP tear-off triggered!")); switch_off(); reply_ng(CMD_HF_MFU_OTP_TEAROFF, PM3_SUCCESS, NULL, 0); } + +// +// Tear-off attack against MFU counter +void MifareU_Counter_Tearoff(uint8_t counter, uint32_t tearoff_time) { + + if (tearoff_time > 43000) + tearoff_time = 43000; + + LEDsoff(); + iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); + clear_trace(); + set_tracing(true); + + // Send MFU counter increase cmd + uint8_t cmd[] = { + MIFARE_ULEV1_INCR_CNT, + counter, + 0, // lsb + 0, + 0, // msb + 0, // rfu + 0, + 0, + }; + AddCrc14A(cmd, sizeof(cmd) - 2); + + // anticollision / select card + if (!iso14443a_select_card(NULL, NULL, NULL, true, 0, true)) { + if (DBGLEVEL >= DBG_ERROR) Dbprintf("Can't select card"); + OnError(1); + return; + }; + + // send + ReaderTransmit(cmd, sizeof(cmd), NULL); + LED_D_ON(); + SpinDelayUsPrecision(tearoff_time); + switch_off(); + + reply_ng(CMD_HF_MFU_COUNTER_TEAROFF, PM3_SUCCESS, NULL, 0); +} diff --git a/armsrc/mifarecmd.h b/armsrc/mifarecmd.h index 58cec9b49..f83f119e2 100644 --- a/armsrc/mifarecmd.h +++ b/armsrc/mifarecmd.h @@ -64,5 +64,5 @@ void Mifare_DES_Auth2(uint32_t arg0, uint8_t *datain); // Tear-off test for MFU void MifareU_Otp_Tearoff(uint8_t arg0, uint32_t arg1, uint8_t *datain); - +void MifareU_Counter_Tearoff(uint8_t counter, uint32_t tearoff_time); #endif diff --git a/client/src/cmdhfmfu.c b/client/src/cmdhfmfu.c index 219c7c27d..a818c8511 100644 --- a/client/src/cmdhfmfu.c +++ b/client/src/cmdhfmfu.c @@ -878,7 +878,7 @@ static int ulev1_print_counters(void) { PrintAndLogEx(INFO, "--- " _CYAN_("Tag Counters")); uint8_t tear[1] = {0}; uint8_t counter[3] = {0, 0, 0}; - uint16_t len = 0; + int len = 0; for (uint8_t i = 0; i < 3; ++i) { ulev1_readTearing(i, tear, sizeof(tear)); len = ulev1_readCounter(i, counter, sizeof(counter)); @@ -2832,10 +2832,12 @@ static int CmdHF14AMfuOtpTearoff(const char *Cmd) { return usage_hf_mfu_otp_tearoff(); case 'b': blockNoUint = param_get8(Cmd, cmdp + 1); +/* if (blockNoUint < 2) { PrintAndLogEx(WARNING, "Wrong block number"); errors = true; } +*/ cmdp += 2; break; case 'i': @@ -3037,6 +3039,141 @@ static int CmdHF14AMfuOtpTearoff(const char *Cmd) { return PM3_SUCCESS; } + +static int CmdHF14AMfuEv1CounterTearoff(const char *Cmd) { + + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mfu countertear", + "Tear-off test against a Ev1 counter", + "hf mfu countertear\n" + "hf mfu countertear -s 200 -l 2500 -> target counter 0, start delay 200\n" + "hf mfu countertear -i 2 -s 200 -l 400 -> target counter 0, start delay 200\n" + ); + + void *argtable[] = { + arg_param_begin, + arg_int0("c", "cnt", "<0,1,2>", "Target this EV1 counter (0,1,2)"), + arg_int0("i", "inc", "", "time interval to increase in each iteration - default 10 us"), + arg_int0("l", "limit", "", "test upper limit time - default 3000 us"), + arg_int0("s", "start", "", "test start time - default 0 us"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + int counter = arg_get_int_def(ctx, 1, 0); + int interval = arg_get_int_def(ctx, 2, 10); + int time_limit = arg_get_int_def(ctx, 3, 3000); + int start_time = arg_get_int_def(ctx, 4, 0); + CLIParserFree(ctx); + + // Validations + if (start_time > (time_limit - interval)) { + PrintAndLogEx(WARNING, "Wrong start time number"); + return PM3_EINVARG; + } + if (time_limit < interval) { + PrintAndLogEx(WARNING, "Wrong time limit number"); + return PM3_EINVARG; + } + if (time_limit > 43000) { + PrintAndLogEx(WARNING, "You can't set delay out of 1..43000 range!"); + return PM3_EINVARG; + } + uint8_t cnt_no = 0; + if (counter < 0 || counter > 2) { + PrintAndLogEx(WARNING, "Counter must 0, 1 or 2"); + return PM3_EINVARG; + } + cnt_no = (uint8_t)counter; + + + PrintAndLogEx(INFO, "----------------- " _CYAN_("MFU Ev1 Counter Tear off") " ---------------------"); + PrintAndLogEx(INFO, "Starting counter tear-off test"); + PrintAndLogEx(INFO, "Target counter no: %u", counter); + PrintAndLogEx(INFO, "----------------------------------------------------"); + + bool got_pre = false, got_post = false; + uint8_t pre[3] = {0}; + uint8_t post[3] = {0}; + uint32_t actual_time = start_time; + + iso14a_card_select_t card; + + while (actual_time <= (time_limit - interval)) { + + if (kbd_enter_pressed()) { + PrintAndLogEx(INFO, "\naborted via keyboard!\n"); + break; + } + + PrintAndLogEx(INFO, "Using tear-off delay " _GREEN_("%" PRIu32) " us", actual_time); + + if (ul_select(&card) == 0) + return PM3_ESOFT; + + got_pre = false; + uint8_t cntresp[3] = {0, 0, 0}; + int rlen = ulev1_readCounter(cnt_no, cntresp, sizeof(cntresp)); + if ( rlen == sizeof(cntresp) ) { + memcpy(pre, cntresp, sizeof(pre)); + got_pre = true; + } + DropField(); + + struct p { + uint8_t counter; + uint32_t tearoff_time; + } PACKED payload; + payload.counter = cnt_no; + payload.tearoff_time = actual_time; + + clearCommandBuffer(); + PacketResponseNG resp; + SendCommandNG(CMD_HF_MFU_COUNTER_TEAROFF, (uint8_t*)&payload, sizeof(payload)); + if (!WaitForResponseTimeout(CMD_HF_MFU_COUNTER_TEAROFF, &resp, 2000)) { + PrintAndLogEx(WARNING, "Failed"); + return PM3_ESOFT; + } + + got_post = false; + if (ul_select(&card) == 0) + return PM3_ESOFT; + + rlen = ulev1_readCounter(cnt_no, cntresp, sizeof(cntresp)); + if ( rlen == sizeof(cntresp) ) { + memcpy(post, cntresp, sizeof(post)); + got_post = true; + } + DropField(); + + if (got_pre && got_post) { + + char prestr[20] = {0}; + snprintf(prestr, sizeof(prestr), "%s", sprint_hex_inrow(pre, sizeof(pre))); + char poststr[20] = {0}; + snprintf(poststr, sizeof(poststr), "%s", sprint_hex_inrow(post, sizeof(post))); + + if (memcmp(pre, post, sizeof(pre)) == 0) { + PrintAndLogEx(INFO, "Current %d - %s", cnt_no, poststr); + } else { + PrintAndLogEx(INFO, _CYAN_("Tear off occured") " : %d %s vs " _RED_("%s") + , cnt_no, prestr, poststr); + } + + } else { + if (got_pre == false) + PrintAndLogEx(FAILED, "Failed to read Counter BEFORE"); + if (got_post == false) + PrintAndLogEx(FAILED, "Failed to read Counter AFTER"); + } + + actual_time += interval; + } + DropField(); + PrintAndLogEx(NORMAL, ""); + return PM3_SUCCESS; +} + + static int CmdHF14MfuNDEF(const char *Cmd) { int keylen; @@ -3167,6 +3304,7 @@ static command_t CommandTable[] = { {"gen", CmdHF14AMfUGenDiverseKeys, AlwaysAvailable, "Generate 3des mifare diversified keys"}, {"pwdgen", CmdHF14AMfUPwdGen, AlwaysAvailable, "Generate pwd from known algos"}, {"otptear", CmdHF14AMfuOtpTearoff, IfPm3Iso14443a, "Tear-off test on OTP bits"}, + {"countertear", CmdHF14AMfuEv1CounterTearoff, IfPm3Iso14443a, "Tear-off test on Ev1 Counter bits"}, {"ndef", CmdHF14MfuNDEF, IfPm3Iso14443a, "Prints NDEF records from card"}, {NULL, NULL, NULL, NULL} }; diff --git a/include/pm3_cmd.h b/include/pm3_cmd.h index 267ca2b7f..3bbedd9c7 100644 --- a/include/pm3_cmd.h +++ b/include/pm3_cmd.h @@ -650,6 +650,9 @@ typedef struct { // MFU OTP TearOff #define CMD_HF_MFU_OTP_TEAROFF 0x0740 +// MFU_Ev1 Counter TearOff +#define CMD_HF_MFU_COUNTER_TEAROFF 0x0741 + #define CMD_HF_SNIFF 0x0800 #define CMD_HF_PLOT 0x0801 From 114cc986000f05d6f1efc1197cf93dea147efbd1 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Mon, 12 Oct 2020 19:32:36 +0200 Subject: [PATCH 12/58] less output --- client/src/cmdhfmfu.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/client/src/cmdhfmfu.c b/client/src/cmdhfmfu.c index a818c8511..a24b2b57f 100644 --- a/client/src/cmdhfmfu.c +++ b/client/src/cmdhfmfu.c @@ -3105,7 +3105,7 @@ static int CmdHF14AMfuEv1CounterTearoff(const char *Cmd) { break; } - PrintAndLogEx(INFO, "Using tear-off delay " _GREEN_("%" PRIu32) " us", actual_time); + PrintAndLogEx(INPLACE, "Using tear-off delay " _GREEN_("%" PRIu32) " us", actual_time); if (ul_select(&card) == 0) return PM3_ESOFT; @@ -3130,7 +3130,7 @@ static int CmdHF14AMfuEv1CounterTearoff(const char *Cmd) { PacketResponseNG resp; SendCommandNG(CMD_HF_MFU_COUNTER_TEAROFF, (uint8_t*)&payload, sizeof(payload)); if (!WaitForResponseTimeout(CMD_HF_MFU_COUNTER_TEAROFF, &resp, 2000)) { - PrintAndLogEx(WARNING, "Failed"); + PrintAndLogEx(WARNING, "\ntear off command failed"); return PM3_ESOFT; } @@ -3153,17 +3153,22 @@ static int CmdHF14AMfuEv1CounterTearoff(const char *Cmd) { snprintf(poststr, sizeof(poststr), "%s", sprint_hex_inrow(post, sizeof(post))); if (memcmp(pre, post, sizeof(pre)) == 0) { - PrintAndLogEx(INFO, "Current %d - %s", cnt_no, poststr); +// PrintAndLogEx(INFO, "Current %d - %s", cnt_no, poststr); } else { + PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, _CYAN_("Tear off occured") " : %d %s vs " _RED_("%s") , cnt_no, prestr, poststr); } } else { - if (got_pre == false) + if (got_pre == false) { + PrintAndLogEx(NORMAL, ""); PrintAndLogEx(FAILED, "Failed to read Counter BEFORE"); - if (got_post == false) + } + if (got_post == false) { + PrintAndLogEx(NORMAL, ""); PrintAndLogEx(FAILED, "Failed to read Counter AFTER"); + } } actual_time += interval; From 2235d96486c7265d4459de36af9f7760ce7c51da Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Mon, 12 Oct 2020 19:54:50 +0200 Subject: [PATCH 13/58] textual --- client/src/cmdhfmfu.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/client/src/cmdhfmfu.c b/client/src/cmdhfmfu.c index a24b2b57f..d1f2792f1 100644 --- a/client/src/cmdhfmfu.c +++ b/client/src/cmdhfmfu.c @@ -3083,12 +3083,27 @@ static int CmdHF14AMfuEv1CounterTearoff(const char *Cmd) { PrintAndLogEx(WARNING, "Counter must 0, 1 or 2"); return PM3_EINVARG; } + cnt_no = (uint8_t)counter; + + iso14a_card_select_t card; + + if (ul_select(&card) == 0) { + PrintAndLogEx(WARNING, "Failed to select card"); + return PM3_ESOFT; + } + uint8_t inital_cnt[3] = {0, 0, 0}; + int len = ulev1_readCounter(cnt_no, inital_cnt, sizeof(inital_cnt)); + DropField(); + if ( len != sizeof(inital_cnt) ) { + PrintAndLogEx(WARNING, "Failed to read counter"); + return PM3_ESOFT; + } PrintAndLogEx(INFO, "----------------- " _CYAN_("MFU Ev1 Counter Tear off") " ---------------------"); - PrintAndLogEx(INFO, "Starting counter tear-off test"); - PrintAndLogEx(INFO, "Target counter no: %u", counter); + PrintAndLogEx(INFO, "Target counter no " _GREEN_("%u"), counter); + PrintAndLogEx(INFO, "Target initial value " _GREEN_("%s"), sprint_hex_inrow(inital_cnt, sizeof(inital_cnt))); PrintAndLogEx(INFO, "----------------------------------------------------"); bool got_pre = false, got_post = false; @@ -3096,8 +3111,6 @@ static int CmdHF14AMfuEv1CounterTearoff(const char *Cmd) { uint8_t post[3] = {0}; uint32_t actual_time = start_time; - iso14a_card_select_t card; - while (actual_time <= (time_limit - interval)) { if (kbd_enter_pressed()) { From a575827c99ac61f7102ed9df4ef713fc889dff02 Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Mon, 12 Oct 2020 21:40:47 +0200 Subject: [PATCH 14/58] add tearoff to hf 14a raw --- armsrc/iso14443a.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index ec5d11d00..19c16f656 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -2929,10 +2929,15 @@ void ReaderIso14443a(PacketCommandNG *c) { ReaderTransmit(cmd, len, NULL); // 8 bits, odd parity } } - arg0 = ReaderReceive(buf, par); - FpgaDisableTracing(); - reply_old(CMD_ACK, arg0, 0, 0, buf, sizeof(buf)); + if (tearoff_hook() == PM3_ETEAROFF) { // tearoff occured + FpgaDisableTracing(); + reply_old(CMD_ACK, 0, 0, 0, NULL, 0); + } else { + arg0 = ReaderReceive(buf, par); + FpgaDisableTracing(); + reply_old(CMD_ACK, arg0, 0, 0, buf, sizeof(buf)); + } } if ((param & ISO14A_REQUEST_TRIGGER)) From 782d36d312dcf99979f2c849472fd746609bd818 Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Mon, 12 Oct 2020 23:41:44 +0200 Subject: [PATCH 15/58] add option to hf mfu cauth to keep field active --- client/src/cmdhfmfu.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/client/src/cmdhfmfu.c b/client/src/cmdhfmfu.c index d1f2792f1..771ae0e41 100644 --- a/client/src/cmdhfmfu.c +++ b/client/src/cmdhfmfu.c @@ -170,7 +170,8 @@ static int usage_hf_mfu_sim(void) { static int usage_hf_mfu_ucauth(void) { PrintAndLogEx(NORMAL, "Tests 3DES password on Mifare Ultralight-C tag."); PrintAndLogEx(NORMAL, "If password is not specified, a set of known defaults will be tested."); - PrintAndLogEx(NORMAL, "Usage: hf mfu cauth "); + PrintAndLogEx(NORMAL, "Usage: hf mfu cauth [k] "); + PrintAndLogEx(NORMAL, " k - keep field on (only if a password is provided too)"); PrintAndLogEx(NORMAL, " [password] - (32 hex symbols)"); PrintAndLogEx(NORMAL, "Examples:"); PrintAndLogEx(NORMAL, _YELLOW_(" hf mfu cauth")); @@ -2398,25 +2399,31 @@ static int CmdHF14AMfUSim(const char *Cmd) { // static int CmdHF14AMfUCAuth(const char *Cmd) { - char cmdp = tolower(param_getchar(Cmd, 0)); - if (cmdp == 'h') { + uint8_t cmdp = 0; + char c = tolower(param_getchar(Cmd, 0)); + if (c == 'h') { return usage_hf_mfu_ucauth(); } + bool keep_field_on = false; + if (c == 'k') { + keep_field_on = true; + cmdp++; + } uint8_t key_buf[16]; uint8_t *key; int succeeded; // If no hex key is specified, try all known ones - if (strlen(Cmd) == 0) { + if (strlen(Cmd + cmdp) == 0) { succeeded = try_default_3des_keys(&key); // Else try user-supplied } else { - if (param_gethex(Cmd, 0, key_buf, 32)) { + if (param_gethex(Cmd, cmdp, key_buf, 32)) { PrintAndLogEx(WARNING, "Password must include 32 HEX symbols"); return PM3_EINVARG; } - succeeded = ulc_authentication(key_buf, true); + succeeded = ulc_authentication(key_buf, ! keep_field_on); key = key_buf; } From d3336ef8422ab9466f818fcd160730d2919b24b1 Mon Sep 17 00:00:00 2001 From: dxl <64101226@qq.com> Date: Tue, 13 Oct 2020 14:26:40 +0800 Subject: [PATCH 16/58] fix text --- client/src/cmdlfem4x50.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/cmdlfem4x50.c b/client/src/cmdlfem4x50.c index 4e608cd42..fa4e2f765 100644 --- a/client/src/cmdlfem4x50.c +++ b/client/src/cmdlfem4x50.c @@ -16,7 +16,7 @@ #include "em4x50.h" static int usage_lf_em4x50_info(void) { - PrintAndLogEx(NORMAL, "Read all information of EM4x50. Tag nust be on antenna."); + PrintAndLogEx(NORMAL, "Read all information of EM4x50. Tag must be on antenna."); PrintAndLogEx(NORMAL, ""); PrintAndLogEx(NORMAL, "Usage: lf em 4x50_info [h] [v] [p ]"); PrintAndLogEx(NORMAL, "Options:"); From 334b3bfb77f3ed856e085efd5e76488d1d24e59b Mon Sep 17 00:00:00 2001 From: dxl <64101226@qq.com> Date: Tue, 13 Oct 2020 14:26:40 +0800 Subject: [PATCH 17/58] fix text --- client/src/cmdlfem4x50.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/cmdlfem4x50.c b/client/src/cmdlfem4x50.c index 4e608cd42..fa4e2f765 100644 --- a/client/src/cmdlfem4x50.c +++ b/client/src/cmdlfem4x50.c @@ -16,7 +16,7 @@ #include "em4x50.h" static int usage_lf_em4x50_info(void) { - PrintAndLogEx(NORMAL, "Read all information of EM4x50. Tag nust be on antenna."); + PrintAndLogEx(NORMAL, "Read all information of EM4x50. Tag must be on antenna."); PrintAndLogEx(NORMAL, ""); PrintAndLogEx(NORMAL, "Usage: lf em 4x50_info [h] [v] [p ]"); PrintAndLogEx(NORMAL, "Options:"); From 1afddd2b1215f754863ac54bdf88dec0419adac0 Mon Sep 17 00:00:00 2001 From: dxl <64101226@qq.com> Date: Tue, 13 Oct 2020 17:27:28 +0800 Subject: [PATCH 18/58] 32 sector print incomplete for Mifare 4K --- client/src/cmdhfmf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/cmdhfmf.c b/client/src/cmdhfmf.c index f197194d4..b842bd7a7 100644 --- a/client/src/cmdhfmf.c +++ b/client/src/cmdhfmf.c @@ -827,7 +827,7 @@ static int CmdHF14AMfRdSc(const char *Cmd) { uint8_t blocks = 4; uint8_t start = sectorNo * 4; - if (sectorNo > 32) { + if (sectorNo >= 32) { blocks = 16; start = 128 + (sectorNo - 32) * 16; } From 1bc14437318724d0fe85fbdf839a265e39db8751 Mon Sep 17 00:00:00 2001 From: dxl <64101226@qq.com> Date: Tue, 13 Oct 2020 17:33:32 +0800 Subject: [PATCH 19/58] 32 sector print Incomplete for Mifare 4K(more) --- client/src/cmdhfmf.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/src/cmdhfmf.c b/client/src/cmdhfmf.c index b842bd7a7..ead78d7e4 100644 --- a/client/src/cmdhfmf.c +++ b/client/src/cmdhfmf.c @@ -3671,7 +3671,7 @@ static int CmdHF14AMfEGetSc(const char *Cmd) { PrintAndLogEx(NORMAL, "----+------------------------------------------------"); uint8_t blocks = 4; uint8_t start = sector * 4; - if (sector > 32) { + if (sector >= 32) { blocks = 16; start = 128 + (sector - 32) * 16; } @@ -4365,7 +4365,7 @@ static int CmdHF14AMfCGetSc(const char *Cmd) { PrintAndLogEx(NORMAL, "----+------------------------------------------------"); uint8_t blocks = 4; uint8_t start = sector * 4; - if (sector > 32) { + if (sector >= 32) { blocks = 16; start = 128 + (sector - 32) * 16; } From 597e069c5001ba9b24e8e80c68e731e4534b98e8 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Tue, 13 Oct 2020 11:54:38 +0200 Subject: [PATCH 20/58] remove HF ICLASS REPLAY, use HF ICLASS RDBL or HF ICLASS DUMP instead with option "n" --- armsrc/appmain.c | 17 ++-- armsrc/iclass.c | 141 ++++++---------------------- armsrc/iclass.h | 1 - client/src/cmdhficlass.c | 197 ++++++++++----------------------------- include/pm3_cmd.h | 2 +- 5 files changed, 86 insertions(+), 272 deletions(-) diff --git a/armsrc/appmain.c b/armsrc/appmain.c index 9aa65de03..0b274f654 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -1512,20 +1512,19 @@ static void PacketReceived(PacketCommandNG *packet) { break; } case CMD_HF_ICLASS_SIMULATE: { - SimulateIClass(packet->oldarg[0], packet->oldarg[1], packet->oldarg[2], packet->data.asBytes); - break; - } - case CMD_HF_ICLASS_READER: { - ReaderIClass(packet->oldarg[0]); - break; - } - case CMD_HF_ICLASS_REPLAY: { +/* struct p { uint8_t reader[4]; uint8_t mac[4]; } PACKED; struct p *payload = (struct p *) packet->data.asBytes; - ReaderIClass_Replay(payload->reader, payload->mac); +*/ + + SimulateIClass(packet->oldarg[0], packet->oldarg[1], packet->oldarg[2], packet->data.asBytes); + break; + } + case CMD_HF_ICLASS_READER: { + ReaderIClass(packet->oldarg[0]); break; } case CMD_HF_ICLASS_EML_MEMSET: { diff --git a/armsrc/iclass.c b/armsrc/iclass.c index 7f22b59cc..baaff65c5 100644 --- a/armsrc/iclass.c +++ b/armsrc/iclass.c @@ -1470,101 +1470,6 @@ void ReaderIClass(uint8_t flags) { switch_off(); } -// turn off afterwards -void ReaderIClass_Replay(uint8_t *rnr, uint8_t *mac) { - - BigBuf_free(); - - uint8_t check[] = { ICLASS_CMD_CHECK, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - - memcpy(check + 1, rnr, 4); - memcpy(check + 5, mac, 4); - - uint8_t *card_data = BigBuf_malloc(ICLASS_16KS_SIZE); - if (card_data == NULL) { - DbpString("fail to allocate memory"); - reply_ng(CMD_HF_ICLASS_REPLAY, PM3_EMALLOC, NULL, 0); - return; - } - memset(card_data, 0xFF, ICLASS_16KS_SIZE); - - uint32_t start_time = 0; - uint32_t eof_time = 0; - - Iso15693InitReader(); - - picopass_hdr hdr = {0}; - bool res = select_iclass_tag((uint8_t *)&hdr, false, &eof_time); - if (res == false) { - reply_ng(CMD_HF_ICLASS_REPLAY, PM3_ETIMEOUT, NULL, 0); - switch_off(); - return; - } - - uint8_t resp[10] = {0}; - - //for now replay captured auth (as cc not updated) - start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; - res = iclass_send_cmd_with_retries(check, sizeof(check), resp, sizeof(resp), 4, 3, &start_time, ICLASS_READER_TIMEOUT_OTHERS, &eof_time); - if (res == false) { - reply_ng(CMD_HF_ICLASS_REPLAY, PM3_ETIMEOUT, NULL, 0); - switch_off(); - return; - } - - uint8_t mem = hdr.conf.mem_config; - uint8_t cardsize = ((mem & 0x80) == 0x80) ? 255 : 32; - - /* - static struct memory_t { - int k16; - int book; - int k2; - int lockauth; - int keyaccess; - } memory; - - // memory.k16 = ((mem & 0x80) == 0x80); - // memory.book = ((mem & 0x20) == 0x20); - // memory.k2 = ((mem & 0x08) == 0x08); - // memory.lockauth = ((mem & 0x02) == 0x02); - // memory.keyaccess = ((mem & 0x01) == 0x01); - // uint8_t cardsize = memory.k16 ? 255 : 32; - */ - - bool dumpsuccess = true; - - // main read loop - uint16_t i; - for (i = 0; i <= cardsize; i++) { - - uint8_t c[] = {ICLASS_CMD_READ_OR_IDENTIFY, i, 0x00, 0x00}; - AddCrc(c + 1, 1); - - res = iclass_send_cmd_with_retries(c, sizeof(c), resp, sizeof(resp), 10, 3, &start_time, ICLASS_READER_TIMEOUT_OTHERS, &eof_time); - if (res) { - memcpy(card_data + (8 * i), resp, 8); - } else { - Dbprintf("failed to read block %u ( 0x%02x)", i, i); - dumpsuccess = false; - } - } - - struct p { - bool isOK; - uint16_t block_cnt; - uint32_t bb_offset; - } PACKED response; - - response.isOK = dumpsuccess; - response.block_cnt = i; - response.bb_offset = card_data - BigBuf_get_addr(); - reply_ng(CMD_HF_ICLASS_REPLAY, PM3_SUCCESS, (uint8_t *)&response, sizeof(response)); - - BigBuf_free(); - switch_off(); -} - // used with function select_and_auth (cmdhficlass.c) // which needs to authenticate before doing more things like read/write // selects and authenticate to a card, sends back div_key and mac to client. @@ -1585,24 +1490,30 @@ bool authenticate_iclass_tag(iclass_auth_req_t *payload, picopass_hdr *hdr, uint memcpy(ccnr, hdr->epurse, sizeof(hdr->epurse)); - if (payload->use_raw) - memcpy(div_key, payload->key, 8); - else - iclass_calc_div_key(hdr->csn, payload->key, div_key, payload->use_elite); + if ( payload->use_replay) { - if (payload->use_credit_key) - memcpy(hdr->key_c, div_key, sizeof(hdr->key_c)); - else - memcpy(hdr->key_d, div_key, sizeof(hdr->key_d)); + memcpy(pmac, payload->key + 4, 4); + memcpy(cmd_check + 1, payload->key, 8); - opt_doReaderMAC(ccnr, div_key, pmac); + } else { + if (payload->use_raw) + memcpy(div_key, payload->key, 8); + else + iclass_calc_div_key(hdr->csn, payload->key, div_key, payload->use_elite); - // copy MAC to check command (readersignature) - cmd_check[5] = pmac[0]; - cmd_check[6] = pmac[1]; - cmd_check[7] = pmac[2]; - cmd_check[8] = pmac[3]; + if (payload->use_credit_key) + memcpy(hdr->key_c, div_key, sizeof(hdr->key_c)); + else + memcpy(hdr->key_d, div_key, sizeof(hdr->key_d)); + opt_doReaderMAC(ccnr, div_key, pmac); + + // copy MAC to check command (readersignature) + cmd_check[5] = pmac[0]; + cmd_check[6] = pmac[1]; + cmd_check[7] = pmac[2]; + cmd_check[8] = pmac[3]; + } return iclass_send_cmd_with_retries(cmd_check, sizeof(cmd_check), resp_auth, sizeof(resp_auth), 4, 2, start_time, ICLASS_READER_TIMEOUT_OTHERS, eof_time); } @@ -1937,10 +1848,14 @@ void iClass_WriteBlock(uint8_t *msg) { wb[0] = payload->req.blockno; memcpy(wb + 1, payload->data, 8); - if (payload->req.use_credit_key) - doMAC_N(wb, sizeof(wb), hdr.key_c, mac); - else - doMAC_N(wb, sizeof(wb), hdr.key_d, mac); + if (payload->req.use_replay) { + doMAC_N(wb, sizeof(wb), payload->req.key + 4, mac); + } else { + if (payload->req.use_credit_key) + doMAC_N(wb, sizeof(wb), hdr.key_c, mac); + else + doMAC_N(wb, sizeof(wb), hdr.key_d, mac); + } memcpy(write + 2, payload->data, 8); // data memcpy(write + 10, mac, sizeof(mac)); // mac diff --git a/armsrc/iclass.h b/armsrc/iclass.h index 3776aa385..ec9ff7b15 100644 --- a/armsrc/iclass.h +++ b/armsrc/iclass.h @@ -17,7 +17,6 @@ void SniffIClass(uint8_t jam_search_len, uint8_t *jam_search_string); void ReaderIClass(uint8_t arg0); -void ReaderIClass_Replay(uint8_t *rnr, uint8_t *mac); void iClass_WriteBlock(uint8_t *msg); void iClass_Dump(uint8_t *msg); diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index 28b710fe8..81bca140e 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -155,10 +155,11 @@ static int usage_hf_iclass_dump(void) { PrintAndLogEx(NORMAL, "Options:"); PrintAndLogEx(NORMAL, " h : Show this help"); PrintAndLogEx(NORMAL, " f : specify a filename to save dump to"); - PrintAndLogEx(NORMAL, " k : access Key as 16 hex symbols or 1 hex to select key from memory"); + PrintAndLogEx(NORMAL, " k : access Key as 16 hex symbols or 1 hex to select key from memory OR NR/MAC for replay"); PrintAndLogEx(NORMAL, " c : credit key as 16 hex symbols or 1 hex to select key from memory"); PrintAndLogEx(NORMAL, " e : elite computations applied to key"); PrintAndLogEx(NORMAL, " r : raw, the key is interpreted as raw block 3/4"); + PrintAndLogEx(NORMAL, " n : replay of NR/MAC"); PrintAndLogEx(NORMAL, " v : verbose output"); PrintAndLogEx(NORMAL, ""); PrintAndLogEx(NORMAL, "Examples:"); @@ -195,10 +196,11 @@ static int usage_hf_iclass_writeblock(void) { PrintAndLogEx(NORMAL, " h : Show this help"); PrintAndLogEx(NORMAL, " b : The block number as 2 hex symbols"); PrintAndLogEx(NORMAL, " d : set the Data to write as 16 hex symbols"); - PrintAndLogEx(NORMAL, " k : access Key as 16 hex symbols or 1 hex to select key from memory"); + PrintAndLogEx(NORMAL, " k : access Key as 16 hex symbols or 1 hex to select key from memory OR NR/MAC for replay"); PrintAndLogEx(NORMAL, " c : credit key assumed\n"); PrintAndLogEx(NORMAL, " e : elite computations applied to key"); PrintAndLogEx(NORMAL, " r : raw, no computations applied to key (raw)"); +// PrintAndLogEx(NORMAL, " n : replay of NR/MAC"); PrintAndLogEx(NORMAL, " v : verbose output"); PrintAndLogEx(NORMAL, "Examples:"); PrintAndLogEx(NORMAL, _YELLOW_("\thf iclass wrbl b 0A d AAAAAAAAAAAAAAAA k 001122334455667B")); @@ -213,10 +215,11 @@ static int usage_hf_iclass_readblock(void) { PrintAndLogEx(NORMAL, "Options:"); PrintAndLogEx(NORMAL, " h : Show this help"); PrintAndLogEx(NORMAL, " b : The block number as 2 hex symbols"); - PrintAndLogEx(NORMAL, " k : Access Key as 16 hex symbols or 1 hex to select key from memory"); + PrintAndLogEx(NORMAL, " k : access Key as 16 hex symbols or 1 hex to select key from memory OR NR/MAC for replay"); PrintAndLogEx(NORMAL, " c : credit key assumed\n"); PrintAndLogEx(NORMAL, " e : elite computations applied to key"); PrintAndLogEx(NORMAL, " r : raw, no computations applied to key"); + PrintAndLogEx(NORMAL, " n : replay of NR/MAC"); PrintAndLogEx(NORMAL, " v : verbose output"); PrintAndLogEx(NORMAL, "Examples:"); PrintAndLogEx(NORMAL, _YELLOW_("\thf iclass rdbl b 06 k 0011223344556677")); @@ -280,18 +283,6 @@ static int usage_hf_iclass_reader(void) { PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; } -static int usage_hf_iclass_replay(void) { - PrintAndLogEx(NORMAL, "Replay a collected mac message\n"); - PrintAndLogEx(NORMAL, "Usage: hf iclass replay [h] [m ]\n"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h Show this help"); - PrintAndLogEx(NORMAL, " r Reader nonce bytes to replay (8 hexsymbols)"); - PrintAndLogEx(NORMAL, " m Mac bytes to replay (8 hexsymbols)"); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_("\thf iclass replay r 00000000 m 89cb984b")); - PrintAndLogEx(NORMAL, ""); - return PM3_SUCCESS; -} static int usage_hf_iclass_loclass(void) { PrintAndLogEx(NORMAL, "Execute the offline part of loclass attack"); PrintAndLogEx(NORMAL, " An iclass dumpfile is assumed to consist of an arbitrary number of"); @@ -346,7 +337,6 @@ static int usage_hf_iclass_lookup(void) { return PM3_SUCCESS; } - static int cmp_uint32(const void *a, const void *b) { const iclass_prekey_t *x = (const iclass_prekey_t *)a; @@ -890,131 +880,6 @@ static int CmdHFiClassReader(const char *Cmd) { return read_iclass_csn(loop_read, true); } -static int CmdHFiClassReader_Replay(const char *Cmd) { - - struct { - uint8_t reader[4]; - uint8_t mac[4]; - } PACKED payload; - - - bool got_rnr, got_mac; - got_rnr = got_mac = false; - bool errors = false; - uint8_t cmdp = 0; - while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { - switch (tolower(param_getchar(Cmd, cmdp))) { - case 'h': { - return usage_hf_iclass_replay(); - } - case 'r': { - if (param_gethex(Cmd, cmdp + 1, payload.reader, 8)) { - PrintAndLogEx(FAILED, "Reader Nr must include 8 HEX symbols"); - errors = true; - } else { - got_rnr = true; - } - cmdp += 2; - break; - } - case 'm': { - if (param_gethex(Cmd, cmdp + 1, payload.mac, 8)) { - PrintAndLogEx(FAILED, "Reader MAC must include 8 HEX symbols"); - errors = true; - } else { - got_mac = true; - } - cmdp += 2; - break; - } - default: { - PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); - errors = true; - break; - } - } - } - - //Validations - if (errors || cmdp == 0) { - return usage_hf_iclass_replay(); - } - - if (got_rnr == false || got_mac == false) { - PrintAndLogEx(FAILED, "Reader Nr and MAC is needed"); - return PM3_EINVARG; - } - - PacketResponseNG resp; - clearCommandBuffer(); - SendCommandNG(CMD_HF_ICLASS_REPLAY, (uint8_t *)&payload, sizeof(payload)); - - while (true) { - PrintAndLogEx(NORMAL, "." NOLF); - - if (kbd_enter_pressed()) { - PrintAndLogEx(WARNING, "\naborted via keyboard!\n"); - DropField(); - return PM3_EOPABORTED; - } - - if (WaitForResponseTimeout(CMD_HF_ICLASS_REPLAY, &resp, 2000)) - break; - } - - PrintAndLogEx(NORMAL, ""); - if (resp.status != PM3_SUCCESS) { - PrintAndLogEx(ERR, "failed to communicate with card"); - return resp.status; - } - - struct p_resp { - bool isOK; - uint16_t block_cnt; - uint32_t bb_offset; - } PACKED; - struct p_resp *packet = (struct p_resp *)resp.data.asBytes; - - if (packet->isOK == false) { - PrintAndLogEx(WARNING, "replay reading blocks failed"); - return PM3_ESOFT; - } - - uint32_t startindex = packet->bb_offset; - uint32_t bytes_got = (packet->block_cnt * 8); - - uint8_t tag_data[0x100 * 8]; - memset(tag_data, 0xFF, sizeof(tag_data)); - - // response ok - now get bigbuf content of the dump - if (!GetFromDevice(BIG_BUF, tag_data, sizeof(tag_data), startindex, NULL, 0, NULL, 2500, false)) { - PrintAndLogEx(WARNING, "command execution time out"); - return PM3_ETIMEOUT; - } - - // print the dump - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, "------+----+-------------------------+----------"); - PrintAndLogEx(INFO, " CSN |0x00| " _GREEN_("%s") "|", sprint_hex(tag_data, 8)); - printIclassDumpContents(tag_data, 1, (bytes_got / 8), bytes_got); - - // use CSN as filename - char filename[FILE_PATH_SIZE] = {0}; - strcat(filename, "hf-iclass-"); - FillFileNameByUID(filename, tag_data, "-dump", 8); - - // save the dump to .bin file - PrintAndLogEx(SUCCESS, "saving dump file - %u blocks read", bytes_got / 8); - saveFile(filename, ".bin", tag_data, bytes_got); - saveFileEML(filename, tag_data, bytes_got, 8); - saveFileJSON(filename, jsfIclass, tag_data, bytes_got, NULL); - - PrintAndLogEx(HINT, "Try `" _YELLOW_("hf iclass decrypt") "` to decrypt dump file"); - PrintAndLogEx(HINT, "Try `" _YELLOW_("hf iclass view") "` to view dump file"); - PrintAndLogEx(NORMAL, ""); - return PM3_SUCCESS; -} - static int CmdHFiClassELoad(const char *Cmd) { DumpFileType_t dftype = BIN; @@ -1271,7 +1136,6 @@ static int CmdHFiClassEView(const char *Cmd) { return PM3_SUCCESS; } - static int CmdHFiClassDecrypt(const char *Cmd) { bool errors = false; @@ -1665,6 +1529,7 @@ static int CmdHFiClassDump(const char *Cmd) { bool have_credit_key = false; bool elite = false; bool rawkey = false; + bool use_replay = false; bool errors = false; bool auth = false; uint8_t cmdp = 0; @@ -1732,12 +1597,23 @@ static int CmdHFiClassDump(const char *Cmd) { rawkey = true; cmdp++; break; + case 'n': + PrintAndLogEx(SUCCESS, "Using " _YELLOW_("replay NR/MAC mode")); + use_replay = true; + cmdp++; + break; default: PrintAndLogEx(WARNING, "Unknown parameter '%c'\n", param_getchar(Cmd, cmdp)); errors = true; break; } } + + if ((use_replay + rawkey + elite) > 0) { + PrintAndLogEx(FAILED, "Can not use a combo of 'e', 'r', 'n'"); + errors = true; + } + if (errors) return usage_hf_iclass_dump(); uint32_t flags = (FLAG_ICLASS_READER_INIT | FLAG_ICLASS_READER_CLEARTRACE); @@ -1808,6 +1684,7 @@ static int CmdHFiClassDump(const char *Cmd) { .req.use_raw = rawkey, .req.use_elite = elite, .req.use_credit_key = false, + .req.use_replay = use_replay, .req.send_reply = true, .req.do_auth = auth, .end_block = app_limit1, @@ -1974,7 +1851,7 @@ write_dump: return PM3_SUCCESS; } -static int iclass_write_block(uint8_t blockno, uint8_t *bldata, uint8_t *KEY, bool use_credit_key, bool elite, bool rawkey, bool verbose) { +static int iclass_write_block(uint8_t blockno, uint8_t *bldata, uint8_t *KEY, bool use_credit_key, bool elite, bool rawkey, bool replay, bool verbose) { /* uint8_t MAC[4] = {0x00, 0x00, 0x00, 0x00}; uint8_t div_key[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; @@ -2017,6 +1894,7 @@ static int iclass_write_block(uint8_t blockno, uint8_t *bldata, uint8_t *KEY, bo .req.use_raw = rawkey, .req.use_elite = elite, .req.use_credit_key = use_credit_key, + .req.use_replay = replay, .req.blockno = blockno, .req.send_reply = true, .req.do_auth = true, @@ -2052,6 +1930,7 @@ static int CmdHFiClass_WriteBlock(const char *Cmd) { bool use_credit_key = false; bool elite = false; bool rawkey = false; + bool use_replay = false; bool errors = false; bool verbose = false; uint8_t cmdp = 0; @@ -2105,6 +1984,13 @@ static int CmdHFiClass_WriteBlock(const char *Cmd) { rawkey = true; cmdp++; break; +/* + case 'n': + PrintAndLogEx(SUCCESS, "Using " _YELLOW_("replay NR/MAC mode")); + use_replay = true; + cmdp++; + break; +*/ case 'v': verbose = true; cmdp++; @@ -2118,9 +2004,14 @@ static int CmdHFiClass_WriteBlock(const char *Cmd) { if (got_blockno == false) errors = true; + if ((use_replay + rawkey + elite) > 0) { + PrintAndLogEx(FAILED, "Can not use a combo of 'e', 'r', 'n'"); + errors = true; + } + if (errors || cmdp < 6) return usage_hf_iclass_writeblock(); - int isok = iclass_write_block(blockno, bldata, KEY, use_credit_key, elite, rawkey, verbose); + int isok = iclass_write_block(blockno, bldata, KEY, use_credit_key, elite, rawkey, use_replay, verbose); if (isok == PM3_SUCCESS) PrintAndLogEx(SUCCESS, "Wrote block %02X successful", blockno); else @@ -2337,12 +2228,13 @@ static int CmdHFiClassRestore(const char *Cmd) { return resp.status; } -static int iclass_read_block(uint8_t *KEY, uint8_t blockno, uint8_t keyType, bool elite, bool rawkey, bool verbose, bool auth, uint8_t *out) { +static int iclass_read_block(uint8_t *KEY, uint8_t blockno, uint8_t keyType, bool elite, bool rawkey, bool replay, bool verbose, bool auth, uint8_t *out) { iclass_auth_req_t payload = { .use_raw = rawkey, .use_elite = elite, .use_credit_key = (keyType == 0x18), + .use_replay = replay, .blockno = blockno, .send_reply = true, .do_auth = auth, @@ -2391,6 +2283,7 @@ static int CmdHFiClass_ReadBlock(const char *Cmd) { bool got_blockno = false; bool elite = false; bool rawkey = false; + bool use_replay = false; bool errors = false; bool auth = false; bool verbose = false; @@ -2439,6 +2332,11 @@ static int CmdHFiClass_ReadBlock(const char *Cmd) { rawkey = true; cmdp++; break; + case 'n': + PrintAndLogEx(SUCCESS, "Using " _YELLOW_("replay NR/MAC mode")); + use_replay = true; + cmdp++; + break; case 'v': verbose = true; cmdp++; @@ -2451,6 +2349,11 @@ static int CmdHFiClass_ReadBlock(const char *Cmd) { } if (got_blockno == false) errors = true; + + if ((use_replay + rawkey + elite) > 0) { + PrintAndLogEx(FAILED, "Can not use a combo of 'e', 'r', 'n'"); + errors = true; + } if (errors) return usage_hf_iclass_readblock(); @@ -2467,7 +2370,7 @@ static int CmdHFiClass_ReadBlock(const char *Cmd) { } uint8_t data[8] = {0}; - int res = iclass_read_block(KEY, blockno, keyType, elite, rawkey, verbose, auth, data); + int res = iclass_read_block(KEY, blockno, keyType, elite, rawkey, use_replay, verbose, auth, data); if (res != PM3_SUCCESS) return res; @@ -3541,8 +3444,6 @@ static command_t CommandTable[] = { {"chk", CmdHFiClassCheckKeys, AlwaysAvailable, "[options..] Check keys"}, {"loclass", CmdHFiClass_loclass, AlwaysAvailable, "[options..] Use loclass to perform bruteforce reader attack"}, {"lookup", CmdHFiClassLookUp, AlwaysAvailable, "[options..] Uses authentication trace to check for key in dictionary file"}, - {"replay", CmdHFiClassReader_Replay, IfPm3Iclass, " Read Picopass / iCLASS tag via replay attack"}, - {"-----------", CmdHelp, AlwaysAvailable, "--------------------- " _CYAN_("simulation") " ---------------------"}, {"sim", CmdHFiClassSim, IfPm3Iclass, "[options..] Simulate iCLASS tag"}, {"eload", CmdHFiClassELoad, IfPm3Iclass, "[f ] Load Picopass / iCLASS dump file into emulator memory"}, diff --git a/include/pm3_cmd.h b/include/pm3_cmd.h index 3bbedd9c7..141f51621 100644 --- a/include/pm3_cmd.h +++ b/include/pm3_cmd.h @@ -306,6 +306,7 @@ typedef struct { bool use_raw; bool use_elite; bool use_credit_key; + bool use_replay; bool send_reply; bool do_auth; uint8_t blockno; @@ -564,7 +565,6 @@ typedef struct { #define CMD_HF_ICLASS_SNIFF 0x0392 #define CMD_HF_ICLASS_SIMULATE 0x0393 #define CMD_HF_ICLASS_READER 0x0394 -#define CMD_HF_ICLASS_REPLAY 0x0395 #define CMD_HF_ICLASS_READBL 0x0396 #define CMD_HF_ICLASS_WRITEBL 0x0397 #define CMD_HF_ICLASS_EML_MEMSET 0x0398 From 57f52a5fdec698e2fbf59baec530497f84851059 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Tue, 13 Oct 2020 11:56:14 +0200 Subject: [PATCH 21/58] text --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e5f00a78..d7bdfe891 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] + - Removed 'hf iclass replay' - use the 'hf iclass dump' or 'hf iclass rdbl' with option "n" instead (@iceman1001). Concept taken from official repo (@pwpiwi) - Add low level support for 14b' aka Innovatron (@doegox) - Add doc/cliparser.md (@mwalker33) - Add `hf 14b apdu` - send APDU over ISO14443B (@iceman1001) From 14fd9a54dfbb90e1f76d8eec730bcc469ef4199e Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Tue, 13 Oct 2020 12:07:33 +0200 Subject: [PATCH 22/58] hf mf rdsc - use fcts instead --- 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 ead78d7e4..c723ea6aa 100644 --- a/client/src/cmdhfmf.c +++ b/client/src/cmdhfmf.c @@ -825,12 +825,9 @@ static int CmdHF14AMfRdSc(const char *Cmd) { PrintAndLogEx(NORMAL, "isOk:%02x", isOK); if (isOK) { - uint8_t blocks = 4; - uint8_t start = sectorNo * 4; - if (sectorNo >= 32) { - blocks = 16; - start = 128 + (sectorNo - 32) * 16; - } + uint8_t blocks = NumBlocksPerSector(sectorNo); + uint8_t start = FirstBlockOfSector(sectorNo); + for (int i = 0; i < blocks; i++) { PrintAndLogEx(NORMAL, "%3d | %s", start + i, sprint_hex(data + (i * 16), 16)); } From 125548a44ca368c57669db80f4e7a686fc734827 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Tue, 13 Oct 2020 12:08:34 +0200 Subject: [PATCH 23/58] hf mf rdsc - use fcts instead --- client/src/cmdhfmf.c | 138 ++++++++++++++++++++++--------------------- 1 file changed, 70 insertions(+), 68 deletions(-) diff --git a/client/src/cmdhfmf.c b/client/src/cmdhfmf.c index c723ea6aa..599884120 100644 --- a/client/src/cmdhfmf.c +++ b/client/src/cmdhfmf.c @@ -627,6 +627,76 @@ static void decode_print_st(uint16_t blockno, uint8_t *data) { } } + +static uint16_t NumOfBlocks(char card) { + switch (card) { + case '0' : + return MIFARE_MINI_MAXBLOCK; + case '1' : + return MIFARE_1K_MAXBLOCK; + case '2' : + return MIFARE_2K_MAXBLOCK; + case '4' : + return MIFARE_4K_MAXBLOCK; + default : + return 0; + } +} + +static uint8_t NumOfSectors(char card) { + switch (card) { + case '0' : + return MIFARE_MINI_MAXSECTOR; + case '1' : + return MIFARE_1K_MAXSECTOR; + case '2' : + return MIFARE_2K_MAXSECTOR; + case '4' : + return MIFARE_4K_MAXSECTOR; + default : + return 0; + } +} + +static uint8_t FirstBlockOfSector(uint8_t sectorNo) { + if (sectorNo < 32) { + return sectorNo * 4; + } else { + return 32 * 4 + (sectorNo - 32) * 16; + } +} + +static uint8_t NumBlocksPerSector(uint8_t sectorNo) { + if (sectorNo < 32) { + return 4; + } else { + return 16; + } +} + +static uint8_t GetSectorFromBlockNo(uint8_t blockNo) { + if (blockNo < 128) + return blockNo / 4; + else + return 32 + ((128 - blockNo) / 16); +} + +static char GetFormatFromSector(uint8_t sectorNo) { + switch (sectorNo) { + case MIFARE_MINI_MAXSECTOR: + return '0'; + case MIFARE_1K_MAXSECTOR: + return '1'; + case MIFARE_2K_MAXSECTOR: + return '2'; + case MIFARE_4K_MAXSECTOR: + return '4'; + default : + return ' '; + } +} + + static int CmdHF14AMfDarkside(const char *Cmd) { uint8_t blockno = 0, key_type = MIFARE_AUTH_KEYA; uint64_t key = 0; @@ -840,74 +910,6 @@ static int CmdHF14AMfRdSc(const char *Cmd) { return PM3_SUCCESS; } -static uint16_t NumOfBlocks(char card) { - switch (card) { - case '0' : - return MIFARE_MINI_MAXBLOCK; - case '1' : - return MIFARE_1K_MAXBLOCK; - case '2' : - return MIFARE_2K_MAXBLOCK; - case '4' : - return MIFARE_4K_MAXBLOCK; - default : - return 0; - } -} - -static uint8_t NumOfSectors(char card) { - switch (card) { - case '0' : - return MIFARE_MINI_MAXSECTOR; - case '1' : - return MIFARE_1K_MAXSECTOR; - case '2' : - return MIFARE_2K_MAXSECTOR; - case '4' : - return MIFARE_4K_MAXSECTOR; - default : - return 0; - } -} - -static uint8_t FirstBlockOfSector(uint8_t sectorNo) { - if (sectorNo < 32) { - return sectorNo * 4; - } else { - return 32 * 4 + (sectorNo - 32) * 16; - } -} - -static uint8_t NumBlocksPerSector(uint8_t sectorNo) { - if (sectorNo < 32) { - return 4; - } else { - return 16; - } -} - -static uint8_t GetSectorFromBlockNo(uint8_t blockNo) { - if (blockNo < 128) - return blockNo / 4; - else - return 32 + ((128 - blockNo) / 16); -} - -static char GetFormatFromSector(uint8_t sectorNo) { - switch (sectorNo) { - case MIFARE_MINI_MAXSECTOR: - return '0'; - case MIFARE_1K_MAXSECTOR: - return '1'; - case MIFARE_2K_MAXSECTOR: - return '2'; - case MIFARE_4K_MAXSECTOR: - return '4'; - default : - return ' '; - } -} - static int FastDumpWithEcFill(uint8_t numsectors) { PacketResponseNG resp; From d8b2dc2ebfbe66e242bb23c0106ceb5ae7b96ced Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Tue, 13 Oct 2020 14:00:03 +0200 Subject: [PATCH 24/58] enable tearoff for LF EM 4x50_WRITE & EM_WRITE_PASSWORD --- armsrc/em4x50.c | 90 +++++++++++++++++++++++++--------------- client/src/cmdlfem4x50.c | 12 +++--- 2 files changed, 63 insertions(+), 39 deletions(-) diff --git a/armsrc/em4x50.c b/armsrc/em4x50.c index 3f0ad9b6b..bce422908 100644 --- a/armsrc/em4x50.c +++ b/armsrc/em4x50.c @@ -14,6 +14,7 @@ #include "lfadc.h" #include "commonutil.h" #include "em4x50.h" +#include "appmain.h" // tear // 4 data bytes // + byte with row parities @@ -470,6 +471,8 @@ static bool find_double_listen_window(bool bcommand) { if (bcommand) { +// SpinDelay(10); + // data transmission from card has to be stopped, because // a commamd shall be issued @@ -816,7 +819,7 @@ void em4x50_info(em4x50_data_t *etd) { status = (bsuccess << 1) + blogin; lf_finalize(); - reply_ng(CMD_ACK, status, (uint8_t *)tag.sectors, 238); + reply_ng(CMD_LF_EM4X50_INFO, status, (uint8_t *)tag.sectors, 238); } void em4x50_read(em4x50_data_t *etd) { @@ -860,14 +863,13 @@ void em4x50_read(em4x50_data_t *etd) { status = (now << 2) + (bsuccess << 1) + blogin; lf_finalize(); - reply_ng(CMD_ACK, status, (uint8_t *)tag.sectors, 238); + reply_ng(CMD_LF_EM4X50_READ, status, (uint8_t *)tag.sectors, 238); } //============================================================================== // write functions //============================================================================== - -static bool write(uint8_t word[4], uint8_t address) { +static int write(uint8_t word[4], uint8_t address) { // writes to specified
@@ -882,17 +884,23 @@ static bool write(uint8_t word[4], uint8_t address) { // send data em4x50_send_word(word); - // wait for T0 * EM4X50_T_TAG_TWA (write access time) - wait_timer(FPGA_TIMER_0, T0 * EM4X50_T_TAG_TWA); + if (tearoff_hook() == PM3_ETEAROFF) { // tearoff occured + reply_ng(CMD_LF_EM4X50_WRITE, PM3_ETEAROFF, NULL, 0); + return PM3_ETEAROFF; + } else { - // look for ACK sequence - if (check_ack(false)) { + // wait for T0 * EM4X50_T_TAG_TWA (write access time) + wait_timer(FPGA_TIMER_0, T0 * EM4X50_T_TAG_TWA); - // now EM4x50 needs T0 * EM4X50_T_TAG_TWEE (EEPROM write time) - // for saving data and should return with ACK - if (check_ack(false)) - return true; + // look for ACK sequence + if (check_ack(false)) { + // now EM4x50 needs T0 * EM4X50_T_TAG_TWEE (EEPROM write time) + // for saving data and should return with ACK + if (check_ack(false)) + return PM3_SUCCESS; + + } } } else { @@ -900,10 +908,10 @@ static bool write(uint8_t word[4], uint8_t address) { Dbprintf("error in command request"); } - return false; + return PM3_ESOFT; } -static bool write_password(uint8_t password[4], uint8_t new_password[4]) { +static int write_password(uint8_t password[4], uint8_t new_password[4]) { // changes password from to @@ -915,23 +923,29 @@ static bool write_password(uint8_t password[4], uint8_t new_password[4]) { // send address data em4x50_send_word(password); - // wait for T0 * EM4x50_T_TAG_TPP (processing pause time) - wait_timer(FPGA_TIMER_0, T0 * EM4X50_T_TAG_TPP); + if (tearoff_hook() == PM3_ETEAROFF) { // tearoff occured + reply_ng(CMD_LF_EM4X50_WRITE, PM3_ETEAROFF, NULL, 0); + return PM3_ETEAROFF; + } else { - // look for ACK sequence and send rm request - // during following listen window - if (check_ack(true)) { + // wait for T0 * EM4x50_T_TAG_TPP (processing pause time) + wait_timer(FPGA_TIMER_0, T0 * EM4X50_T_TAG_TPP); - // send new password - em4x50_send_word(new_password); + // look for ACK sequence and send rm request + // during following listen window + if (check_ack(true)) { - // wait for T0 * EM4X50_T_TAG_TWA (write access time) - wait_timer(FPGA_TIMER_0, T0 * EM4X50_T_TAG_TWA); + // send new password + em4x50_send_word(new_password); + + // wait for T0 * EM4X50_T_TAG_TWA (write access time) + wait_timer(FPGA_TIMER_0, T0 * EM4X50_T_TAG_TWA); - if (check_ack(false)) if (check_ack(false)) - return true; + if (check_ack(false)) + return PM3_SUCCESS; + } } } else { @@ -939,7 +953,7 @@ static bool write_password(uint8_t password[4], uint8_t new_password[4]) { Dbprintf("error in command request"); } - return false; + return PM3_ESOFT; } void em4x50_write(em4x50_data_t *etd) { @@ -966,8 +980,13 @@ void em4x50_write(em4x50_data_t *etd) { blogin = login(etd->password); // write word to given address - if (write(etd->word, etd->address)) { - + int res = write(etd->word, etd->address); + if (res == PM3_ETEAROFF) { + lf_finalize(); + return; + } + + if (res == PM3_SUCCESS) { // to verify result reset EM4x50 if (reset()) { @@ -996,9 +1015,8 @@ void em4x50_write(em4x50_data_t *etd) { } status = (bsuccess << 1) + blogin; - lf_finalize(); - reply_ng(CMD_ACK, status, (uint8_t *)tag.sectors, 238); + reply_ng(CMD_LF_EM4X50_WRITE, status, (uint8_t *)tag.sectors, 238); } void em4x50_write_password(em4x50_data_t *etd) { @@ -1015,12 +1033,18 @@ void em4x50_write_password(em4x50_data_t *etd) { // login and change password if (login(etd->password)) { - bsuccess = write_password(etd->password, etd->new_password); + + int res = write_password(etd->password, etd->new_password); + if (res == PM3_ETEAROFF) { + lf_finalize(); + return; + } + bsuccess = (res == PM3_SUCCESS); } } lf_finalize(); - reply_ng(CMD_ACK, bsuccess, 0, 0); + reply_ng(CMD_LF_EM4X50_WRITE_PASSWORD, bsuccess, 0, 0); } void em4x50_wipe(em4x50_data_t *etd) { @@ -1078,5 +1102,5 @@ void em4x50_wipe(em4x50_data_t *etd) { } lf_finalize(); - reply_ng(CMD_ACK, bsuccess, (uint8_t *)tag.sectors, 238); + reply_ng(CMD_LF_EM4X50_WIPE, bsuccess, (uint8_t *)tag.sectors, 238); } diff --git a/client/src/cmdlfem4x50.c b/client/src/cmdlfem4x50.c index fa4e2f765..3bddc2113 100644 --- a/client/src/cmdlfem4x50.c +++ b/client/src/cmdlfem4x50.c @@ -315,7 +315,7 @@ int CmdEM4x50Info(const char *Cmd) { SendCommandNG(CMD_LF_EM4X50_INFO, (uint8_t *)&etd, sizeof(etd)); PacketResponseNG resp; - if (!WaitForResponseTimeout(CMD_ACK, &resp, TIMEOUT)) { + if (!WaitForResponseTimeout(CMD_LF_EM4X50_INFO, &resp, TIMEOUT)) { PrintAndLogEx(WARNING, "timeout while waiting for reply."); return PM3_ETIMEOUT; } @@ -388,7 +388,7 @@ int CmdEM4x50Write(const char *Cmd) { clearCommandBuffer(); SendCommandNG(CMD_LF_EM4X50_WRITE, (uint8_t *)&etd, sizeof(etd)); PacketResponseNG resp; - if (!WaitForResponseTimeout(CMD_ACK, &resp, TIMEOUT)) { + if (!WaitForResponseTimeout(CMD_LF_EM4X50_WRITE, &resp, TIMEOUT)) { PrintAndLogEx(WARNING, "timeout while waiting for reply."); return PM3_ETIMEOUT; } @@ -483,7 +483,7 @@ int CmdEM4x50WritePassword(const char *Cmd) { clearCommandBuffer(); SendCommandNG(CMD_LF_EM4X50_WRITE_PASSWORD, (uint8_t *)&etd, sizeof(etd)); - if (!WaitForResponseTimeout(CMD_ACK, &resp, TIMEOUT)) { + if (!WaitForResponseTimeout(CMD_LF_EM4X50_WRITE_PASSWORD, &resp, TIMEOUT)) { PrintAndLogEx(WARNING, "timeout while waiting for reply."); return PM3_ETIMEOUT; } @@ -515,7 +515,7 @@ int em4x50_read(em4x50_data_t *etd, em4x50_word_t *out, bool verbose) { SendCommandNG(CMD_LF_EM4X50_READ, (uint8_t *)&edata, sizeof(edata)); PacketResponseNG resp; - if (!WaitForResponseTimeout(CMD_ACK, &resp, TIMEOUT)) { + if (!WaitForResponseTimeout(CMD_LF_EM4X50_READ, &resp, TIMEOUT)) { PrintAndLogEx(WARNING, "(em4x50) timeout while waiting for reply."); return PM3_ETIMEOUT; } @@ -651,7 +651,7 @@ int CmdEM4x50Dump(const char *Cmd) { clearCommandBuffer(); SendCommandNG(CMD_LF_EM4X50_INFO, (uint8_t *)&etd, sizeof(etd)); PacketResponseNG resp; - if (!WaitForResponseTimeout(CMD_ACK, &resp, TIMEOUT)) { + if (!WaitForResponseTimeout(CMD_LF_EM4X50_INFO, &resp, TIMEOUT)) { PrintAndLogEx(WARNING, "timeout while waiting for reply."); return PM3_ETIMEOUT; } @@ -726,7 +726,7 @@ int CmdEM4x50Wipe(const char *Cmd) { clearCommandBuffer(); SendCommandNG(CMD_LF_EM4X50_WIPE, (uint8_t *)&etd, sizeof(etd)); - if (!WaitForResponseTimeout(CMD_ACK, &resp, 2 * TIMEOUT)) { + if (!WaitForResponseTimeout(CMD_LF_EM4X50_WIPE, &resp, 2 * TIMEOUT)) { PrintAndLogEx(WARNING, "\ntimeout while waiting for reply.\n"); return PM3_ETIMEOUT; } From 9a3c669e593f6e209292204f8fe881d8864cfde0 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Tue, 13 Oct 2020 14:05:18 +0200 Subject: [PATCH 25/58] EM4x50 to exit if tear off --- client/src/cmdlfem4x50.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/client/src/cmdlfem4x50.c b/client/src/cmdlfem4x50.c index 3bddc2113..7296ccbb1 100644 --- a/client/src/cmdlfem4x50.c +++ b/client/src/cmdlfem4x50.c @@ -393,6 +393,9 @@ int CmdEM4x50Write(const char *Cmd) { return PM3_ETIMEOUT; } + if (resp.status == PM3_ETEAROFF) + return PM3_SUCCESS; + bool isOK = (resp.status & STATUS_SUCCESS) >> 1; if (isOK == false) { PrintAndLogEx(FAILED, "writing " _RED_("failed")); @@ -487,6 +490,10 @@ int CmdEM4x50WritePassword(const char *Cmd) { PrintAndLogEx(WARNING, "timeout while waiting for reply."); return PM3_ETIMEOUT; } + + if (resp.status == PM3_ETEAROFF) + return PM3_SUCCESS; + success = (bool)resp.status; // get, prepare and print response From 6e6c8cbd6be0ef645d9344ba4d5d4bd99a1cc451 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Tue, 13 Oct 2020 16:09:17 +0200 Subject: [PATCH 26/58] hf 14b raw - added tearoff --- armsrc/iso14443a.c | 10 +++++----- armsrc/iso14443b.c | 15 ++++++++++----- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index 19c16f656..d40080ae1 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -2624,13 +2624,13 @@ int iso14443a_select_card(uint8_t *uid_ptr, iso14a_card_select_t *p_card, uint32 } // else force RATS // RATS, Request for answer to select - if (!no_rats) { - uint8_t rats[] = { ISO14443A_CMD_RATS, 0x80, 0x00, 0x00 }; // FSD=256, FSDI=8, CID=0 + if (no_rats == false) { + uint8_t rats[] = { ISO14443A_CMD_RATS, 0x80, 0x00, 0x00 }; // FSD=256, FSDI=8, CID=0 AddCrc14A(rats, 2); ReaderTransmit(rats, sizeof(rats), NULL); int len = ReaderReceive(resp, resp_par); - - if (!len) return 0; + if (len == 0) + return 0; if (p_card) { memcpy(p_card->ats, resp, sizeof(p_card->ats)); @@ -2932,7 +2932,7 @@ void ReaderIso14443a(PacketCommandNG *c) { if (tearoff_hook() == PM3_ETEAROFF) { // tearoff occured FpgaDisableTracing(); - reply_old(CMD_ACK, 0, 0, 0, NULL, 0); + reply_mix(CMD_ACK, 0, 0, 0, NULL, 0); } else { arg0 = ReaderReceive(buf, par); FpgaDisableTracing(); diff --git a/armsrc/iso14443b.c b/armsrc/iso14443b.c index 976e13148..64c0ba67f 100644 --- a/armsrc/iso14443b.c +++ b/armsrc/iso14443b.c @@ -1895,12 +1895,17 @@ void SendRawCommand14443B_Ex(PacketCommandNG *c) { uint32_t eof_time = 0; CodeAndTransmit14443bAsReader(cmd, len, &start_time, &eof_time); - eof_time += DELAY_ISO14443B_VCD_TO_VICC_READER; - status = Get14443bAnswerFromTag(buf, sizeof(buf), 5 * ISO14443B_READER_TIMEOUT, &eof_time); // raw - FpgaDisableTracing(); + if (tearoff_hook() == PM3_ETEAROFF) { // tearoff occured + FpgaDisableTracing(); + reply_mix(CMD_HF_ISO14443B_COMMAND, -2, 0, 0, NULL, 0); + } else { + eof_time += DELAY_ISO14443B_VCD_TO_VICC_READER; + status = Get14443bAnswerFromTag(buf, sizeof(buf), 5 * ISO14443B_READER_TIMEOUT, &eof_time); // raw + FpgaDisableTracing(); - sendlen = MIN(Demod.len, PM3_CMD_DATA_SIZE); - reply_mix(CMD_HF_ISO14443B_COMMAND, status, sendlen, 0, Demod.output, sendlen); + sendlen = MIN(Demod.len, PM3_CMD_DATA_SIZE); + reply_mix(CMD_HF_ISO14443B_COMMAND, status, sendlen, 0, Demod.output, sendlen); + } } out: From 045f90ecf418e2403279c52b6c5fae5562493cb5 Mon Sep 17 00:00:00 2001 From: tcprst Date: Tue, 13 Oct 2020 11:19:18 -0400 Subject: [PATCH 27/58] fix fido2 list/info --- client/src/cmdhffido.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/cmdhffido.c b/client/src/cmdhffido.c index 009a14e9f..eac2c71c5 100644 --- a/client/src/cmdhffido.c +++ b/client/src/cmdhffido.c @@ -915,7 +915,7 @@ static int cmd_hf_fido_2get_assertion(const char *cmd) { static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help."}, - {"info", cmd_hf_fido_list, IfPm3Iso14443a, "List ISO 14443A history"}, + {"list", cmd_hf_fido_list, IfPm3Iso14443a, "List ISO 14443A history"}, {"info", cmd_hf_fido_info, IfPm3Iso14443a, "Info about FIDO tag."}, {"reg", cmd_hf_fido_register, IfPm3Iso14443a, "FIDO U2F Registration Message."}, {"auth", cmd_hf_fido_authenticate, IfPm3Iso14443a, "FIDO U2F Authentication Message."}, From 3868b0b4e69fdd316d3f0664edb1f367c263d1d7 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Tue, 13 Oct 2020 22:43:28 +0200 Subject: [PATCH 28/58] hf iclass write, hf 15 raw, write, etc supports tear off trigger --- armsrc/iclass.c | 30 +++++++- armsrc/iso15693.c | 148 ++++++++++++++++++++------------------- client/src/cmdhf15.c | 44 ++++++++++-- client/src/cmdhficlass.c | 19 +++-- 4 files changed, 156 insertions(+), 85 deletions(-) diff --git a/armsrc/iclass.c b/armsrc/iclass.c index baaff65c5..00e88f405 100644 --- a/armsrc/iclass.c +++ b/armsrc/iclass.c @@ -1864,8 +1864,34 @@ void iClass_WriteBlock(uint8_t *msg) { start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; uint8_t resp[10] = {0}; - res = iclass_send_cmd_with_retries(write, sizeof(write), resp, sizeof(resp), 10, 3, &start_time, ICLASS_READER_TIMEOUT_UPDATE, &eof_time); - if (res == false) { + + uint8_t tries = 3; + while (tries-- > 0) { + + iclass_send_as_reader(write, sizeof(write), &start_time, &eof_time); + + if (tearoff_hook() == PM3_ETEAROFF) { // tearoff occured + res = false; + switch_off(); + if (payload->req.send_reply) + reply_ng(CMD_HF_ICLASS_WRITEBL, PM3_ETEAROFF, (uint8_t *)&res, sizeof(uint8_t)); + return; + } else { + + if (resp == NULL) { + res = true; + break; + } + + if (GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_UPDATE, &eof_time) == 10) { + res = true; + break; + } + } + } + + if (tries == 0) { + res = false; goto out; } diff --git a/armsrc/iso15693.c b/armsrc/iso15693.c index e84edd8a0..00af14c4f 100644 --- a/armsrc/iso15693.c +++ b/armsrc/iso15693.c @@ -290,6 +290,7 @@ void TransmitTo15693Tag(const uint8_t *cmd, int len, uint32_t *start_time) { LED_B_OFF(); *start_time = *start_time + DELAY_ARM_TO_TAG; + FpgaDisableTracing(); } //----------------------------------------------------------------------------- @@ -732,6 +733,7 @@ int GetIso15693AnswerFromTag(uint8_t *response, uint16_t max_len, uint16_t timeo } FpgaDisableSscDma(); + FpgaDisableTracing(); uint32_t sof_time = *eof_time - (dt->len * 8 * 8 * 16) // time for byte transfers @@ -1469,17 +1471,22 @@ int SendDataTag(uint8_t *send, int sendlen, bool init, bool speed_fast, uint8_t // low speed (1 out of 256) CodeIso15693AsReader256(send, sendlen); } - + int res = 0; tosend_t *ts = get_tosend(); TransmitTo15693Tag(ts->buf, ts->max, &start_time); - *eof_time = start_time + 32 * ((8 * ts->max) - 4); // substract the 4 padding bits after EOF - LogTrace_ISO15693(send, sendlen, (start_time * 4), (*eof_time * 4), NULL, true); + + if (tearoff_hook() == PM3_ETEAROFF) { // tearoff occured - int res = 0; - if (recv != NULL) { - res = GetIso15693AnswerFromTag(recv, max_recv_len, timeout, eof_time); + res = PM3_ETEAROFF; + + } else { + + *eof_time = start_time + 32 * ((8 * ts->max) - 4); // substract the 4 padding bits after EOF + LogTrace_ISO15693(send, sendlen, (start_time * 4), (*eof_time * 4), NULL, true); + if (recv != NULL) { + res = GetIso15693AnswerFromTag(recv, max_recv_len, timeout, eof_time); + } } - FpgaDisableTracing(); return res; } @@ -1495,7 +1502,6 @@ int SendDataTagEOF(uint8_t *recv, uint16_t max_recv_len, uint32_t start_time, ui if (recv != NULL) { res = GetIso15693AnswerFromTag(recv, max_recv_len, timeout, eof_time); } - FpgaDisableTracing(); return res; } @@ -1588,41 +1594,49 @@ void ReaderIso15693(uint32_t parameter) { BuildIdentifyRequest(cmd); uint32_t start_time = 0; uint32_t eof_time; - int answerLen = SendDataTag(cmd, sizeof(cmd), true, true, answer, ISO15693_MAX_RESPONSE_LENGTH, start_time, ISO15693_READER_TIMEOUT, &eof_time); - start_time = eof_time + DELAY_ISO15693_VICC_TO_VCD_READER; + int recvlen = SendDataTag(cmd, sizeof(cmd), true, true, answer, ISO15693_MAX_RESPONSE_LENGTH, start_time, ISO15693_READER_TIMEOUT, &eof_time); + + if (recvlen == PM3_ETEAROFF) { // tearoff occured + reply_mix(CMD_ACK, recvlen, 0, 0, NULL, 0); + } else { + + start_time = eof_time + DELAY_ISO15693_VICC_TO_VCD_READER; - // we should do a better check than this - if (answerLen >= 12) { - uint8_t uid[8]; - uid[0] = answer[9]; // always E0 - uid[1] = answer[8]; // IC Manufacturer code - uid[2] = answer[7]; - uid[3] = answer[6]; - uid[4] = answer[5]; - uid[5] = answer[4]; - uid[6] = answer[3]; - uid[7] = answer[2]; + // we should do a better check than this + if (recvlen >= 12) { + uint8_t uid[8]; + uid[0] = answer[9]; // always E0 + uid[1] = answer[8]; // IC Manufacturer code + uid[2] = answer[7]; + uid[3] = answer[6]; + uid[4] = answer[5]; + uid[5] = answer[4]; + uid[6] = answer[3]; + uid[7] = answer[2]; - if (DBGLEVEL >= DBG_EXTENDED) { - Dbprintf("[+] UID = %02X%02X%02X%02X%02X%02X%02X%02X", - uid[0], uid[1], uid[2], uid[3], - uid[4], uid[5], uid[5], uid[6] - ); + if (DBGLEVEL >= DBG_EXTENDED) { + Dbprintf("[+] UID = %02X%02X%02X%02X%02X%02X%02X%02X", + uid[0], uid[1], uid[2], uid[3], + uid[4], uid[5], uid[5], uid[6] + ); + } + // send UID back to client. + // arg0 = 1 = OK + // arg1 = len of response (12 bytes) + // arg2 = rtf + // asbytes = uid. + reply_mix(CMD_ACK, 1, sizeof(uid), 0, uid, sizeof(uid)); + + if (DBGLEVEL >= DBG_EXTENDED) { + Dbprintf("[+] %d octets read from IDENTIFY request:", recvlen); + DbdecodeIso15693Answer(recvlen, answer); + Dbhexdump(recvlen, answer, true); + } + } else { + DbpString("Failed to select card"); + reply_mix(CMD_ACK, 0, 0, 0, NULL, 0); } - // send UID back to client. - // arg0 = 1 = OK - // arg1 = len of response (12 bytes) - // arg2 = rtf - // asbytes = uid. - reply_mix(CMD_ACK, 1, sizeof(uid), 0, uid, sizeof(uid)); } - - if (DBGLEVEL >= DBG_EXTENDED) { - Dbprintf("[+] %d octets read from IDENTIFY request:", answerLen); - DbdecodeIso15693Answer(answerLen, answer); - Dbhexdump(answerLen, answer, true); - } - switch_off(); BigBuf_free(); } @@ -1767,6 +1781,11 @@ void BruteforceIso15693Afi(uint32_t speed) { if (recvlen >= 12) { Dbprintf("NoAFI UID = %s", iso15693_sprintUID(NULL, recv + 2)); + } else { + DbpString("Failed to select card"); + reply_ng(CMD_ACK, PM3_ESOFT, NULL, 0); + switch_off(); + return; } // now with AFI @@ -1816,10 +1835,9 @@ void DirectTag15693Command(uint32_t datalen, uint32_t speed, uint32_t recv, uint LED_A_ON(); - int recvlen = 0; uint8_t recvbuf[ISO15693_MAX_RESPONSE_LENGTH]; - uint32_t eof_time = 0; uint16_t timeout; + uint32_t eof_time = 0; bool request_answer = false; switch (data[1]) { @@ -1837,43 +1855,29 @@ void DirectTag15693Command(uint32_t datalen, uint32_t speed, uint32_t recv, uint timeout = ISO15693_READER_TIMEOUT; } - if (DBGLEVEL >= DBG_EXTENDED) { - Dbprintf("SEND:"); - Dbhexdump(datalen, data, false); - } - uint32_t start_time = 0; - recvlen = SendDataTag(data, datalen, true, speed, (recv ? recvbuf : NULL), sizeof(recvbuf), start_time, timeout, &eof_time); + int recvlen = SendDataTag(data, datalen, true, speed, (recv ? recvbuf : NULL), sizeof(recvbuf), start_time, timeout, &eof_time); - // send a single EOF to get the tag response - if (request_answer) { - start_time = eof_time + DELAY_ISO15693_VICC_TO_VCD_READER; - recvlen = SendDataTagEOF((recv ? recvbuf : NULL), sizeof(recvbuf), start_time, ISO15693_READER_TIMEOUT, &eof_time); + if (recvlen == PM3_ETEAROFF) { // tearoff occured + reply_mix(CMD_ACK, recvlen, 0, 0, NULL, 0); + } else { + + // send a single EOF to get the tag response + if (request_answer) { + start_time = eof_time + DELAY_ISO15693_VICC_TO_VCD_READER; + recvlen = SendDataTagEOF((recv ? recvbuf : NULL), sizeof(recvbuf), start_time, ISO15693_READER_TIMEOUT, &eof_time); + } + + if (recv) { + recvlen = MIN(recvlen,ISO15693_MAX_RESPONSE_LENGTH); + reply_mix(CMD_ACK, recvlen, 0, 0, recvbuf, recvlen); + } else { + reply_mix(CMD_ACK, 1, 0, 0, NULL, 0); + } } - - // for the time being, switch field off to protect rdv4.0 // note: this prevents using hf 15 cmd with s option - which isn't implemented yet anyway FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LED_D_OFF(); - - if (recv) { - - if (recvlen > ISO15693_MAX_RESPONSE_LENGTH) { - recvlen = ISO15693_MAX_RESPONSE_LENGTH; - } - reply_mix(CMD_ACK, recvlen, 0, 0, recvbuf, recvlen); - - if (DBGLEVEL >= DBG_EXTENDED) { - - Dbprintf("RECV:"); - if (recvlen > 0) { - Dbhexdump(recvlen, recvbuf, false); - DbdecodeIso15693Answer(recvlen, recvbuf); - } - } - } else { - reply_mix(CMD_ACK, 1, 0, 0, 0, 0); - } } /* diff --git a/client/src/cmdhf15.c b/client/src/cmdhf15.c index 238aad435..407815a93 100644 --- a/client/src/cmdhf15.c +++ b/client/src/cmdhf15.c @@ -323,7 +323,7 @@ static int usage_15_raw(void) { return PM3_SUCCESS; } static int usage_15_read(void) { - PrintAndLogEx(NORMAL, "Usage: hf 15 read [options] \n" + PrintAndLogEx(NORMAL, "Usage: hf 15 rdbl [options] \n" "Options:\n" "\t-2 use slower '1 out of 256' mode\n" "\tuid (either): \n" @@ -334,7 +334,7 @@ static int usage_15_read(void) { return PM3_SUCCESS; } static int usage_15_write(void) { - PrintAndLogEx(NORMAL, "Usage: hf 15 write [options] \n" + PrintAndLogEx(NORMAL, "Usage: hf 15 wrbl [options] \n" "Options:\n" "\t-2 use slower '1 out of 256' mode\n" "\t-o set OPTION Flag (needed for TI)\n" @@ -816,6 +816,10 @@ static int NxpSysInfo(uint8_t *uid) { DropField(); int status = resp.oldarg[0]; + if (status == PM3_ETEAROFF) { + return status; + } + if (status < 2) { PrintAndLogEx(WARNING, "iso15693 card doesn't answer to NXP systeminfo command"); return PM3_EWRONGANSWER; @@ -975,6 +979,9 @@ static int CmdHF15Info(const char *Cmd) { DropField(); int status = resp.oldarg[0]; + if (status == PM3_ETEAROFF) { + return status; + } if (status < 2) { PrintAndLogEx(WARNING, "iso15693 card doesn't answer to systeminfo command (%d)", status); return PM3_EWRONGANSWER; @@ -1153,9 +1160,13 @@ static int CmdHF15WriteAfi(const char *Cmd) { DropField(); return PM3_ETIMEOUT; } - DropField(); + int status = resp.oldarg[0]; + if (status == PM3_ETEAROFF) { + return status; + } + uint8_t *data = resp.data.asBytes; if ((data[0] & ISO15_RES_ERROR) == ISO15_RES_ERROR) { @@ -1212,6 +1223,10 @@ static int CmdHF15WriteDsfid(const char *Cmd) { } DropField(); + int status = resp.oldarg[0]; + if (status == PM3_ETEAROFF) { + return status; + } uint8_t *data = resp.data.asBytes; @@ -1296,6 +1311,9 @@ static int CmdHF15Dump(const char *Cmd) { if (WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { int len = resp.oldarg[0]; + if (len == PM3_ETEAROFF) { + continue; + } if (len < 2) { PrintAndLogEx(FAILED, "iso15693 command failed"); continue; @@ -1321,6 +1339,7 @@ static int CmdHF15Dump(const char *Cmd) { blocknum++; PrintAndLogEx(NORMAL, "." NOLF); + fflush(stdout); } } @@ -1419,6 +1438,10 @@ static int CmdHF15Raw(const char *Cmd) { if (reply) { if (WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { int len = resp.oldarg[0]; + if (len == PM3_ETEAROFF) { + DropField(); + return len; + } if (len < 2) { PrintAndLogEx(WARNING, "command failed"); } else { @@ -1491,6 +1514,10 @@ static int CmdHF15Readmulti(const char *Cmd) { DropField(); int status = resp.oldarg[0]; + if (status == PM3_ETEAROFF) { + return status; + } + if (status < 2) { PrintAndLogEx(FAILED, "iso15693 card readmulti failed"); return PM3_EWRONGANSWER; @@ -1574,6 +1601,9 @@ static int CmdHF15Read(const char *Cmd) { DropField(); int status = resp.oldarg[0]; + if (status == PM3_ETEAROFF) { + return status; + } if (status < 2) { PrintAndLogEx(ERR, "iso15693 command failed"); return PM3_EWRONGANSWER; @@ -1661,6 +1691,10 @@ static int CmdHF15Write(const char *Cmd) { DropField(); int status = resp.oldarg[0]; + if (status == PM3_ETEAROFF) { + return status; + } + if (status < 2) { PrintAndLogEx(FAILED, "iso15693 command failed"); return PM3_EWRONGANSWER; @@ -1876,13 +1910,13 @@ static command_t CommandTable[] = { {"info", CmdHF15Info, IfPm3Iso15693, "Tag information"}, {"sniff", CmdHF15Sniff, IfPm3Iso15693, "Sniff ISO15693 traffic"}, {"raw", CmdHF15Raw, IfPm3Iso15693, "Send raw hex data to tag"}, - {"read", CmdHF15Read, IfPm3Iso15693, "Read a block"}, + {"rdbl", CmdHF15Read, IfPm3Iso15693, "Read a block"}, {"reader", CmdHF15Reader, IfPm3Iso15693, "Act like an ISO15693 reader"}, {"readmulti", CmdHF15Readmulti, IfPm3Iso15693, "Reads multiple Blocks"}, {"restore", CmdHF15Restore, IfPm3Iso15693, "Restore from file to all memory pages of an ISO15693 tag"}, {"samples", CmdHF15Samples, IfPm3Iso15693, "Acquire Samples as Reader (enables carrier, sends inquiry)"}, {"sim", CmdHF15Sim, IfPm3Iso15693, "Fake an ISO15693 tag"}, - {"write", CmdHF15Write, IfPm3Iso15693, "Write a block"}, + {"wrbl", CmdHF15Write, IfPm3Iso15693, "Write a block"}, {"-----------", CmdHF15Help, IfPm3Iso15693, "----------------------- " _CYAN_("afi") " -----------------------"}, {"findafi", CmdHF15FindAfi, IfPm3Iso15693, "Brute force AFI of an ISO15693 tag"}, {"writeafi", CmdHF15WriteAfi, IfPm3Iso15693, "Writes the AFI on an ISO15693 tag"}, diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index 81bca140e..bf40e2d1e 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -1913,9 +1913,8 @@ static int iclass_write_block(uint8_t blockno, uint8_t *bldata, uint8_t *KEY, bo if (resp.status != PM3_SUCCESS) { if (verbose) PrintAndLogEx(ERR, "failed to communicate with card"); - return PM3_EWRONGANSWER; + return resp.status; } - return (resp.data.asBytes[0] == 1) ? PM3_SUCCESS : PM3_ESOFT; } @@ -2012,10 +2011,18 @@ static int CmdHFiClass_WriteBlock(const char *Cmd) { if (errors || cmdp < 6) return usage_hf_iclass_writeblock(); int isok = iclass_write_block(blockno, bldata, KEY, use_credit_key, elite, rawkey, use_replay, verbose); - if (isok == PM3_SUCCESS) - PrintAndLogEx(SUCCESS, "Wrote block %02X successful", blockno); - else - PrintAndLogEx(FAILED, "Writing failed"); + switch(isok) { + case PM3_SUCCESS: + PrintAndLogEx(SUCCESS, "Wrote block %02X successful", blockno); + break; + case PM3_ETEAROFF: + if (verbose) + PrintAndLogEx(INFO, "Writing tear off triggered"); + break; + default: + PrintAndLogEx(FAILED, "Writing failed"); + break; + } return isok; } From 53c7e47e75eb1503bb93a66a1300a08ec5dc2e84 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Wed, 14 Oct 2020 17:41:34 +0200 Subject: [PATCH 29/58] fix: hf iclass restore - now uses NG and better reporting and works :) --- armsrc/Standalone/hf_iceclass.c | 8 +- armsrc/appmain.c | 12 +-- armsrc/iclass.c | 115 ++++++++++++++++------- armsrc/iclass.h | 5 +- client/src/cmdhficlass.c | 161 +++++++++----------------------- include/pm3_cmd.h | 17 ++-- 6 files changed, 143 insertions(+), 175 deletions(-) diff --git a/armsrc/Standalone/hf_iceclass.c b/armsrc/Standalone/hf_iceclass.c index 00de115e6..0c06bc90b 100644 --- a/armsrc/Standalone/hf_iceclass.c +++ b/armsrc/Standalone/hf_iceclass.c @@ -302,15 +302,17 @@ static int reader_dump_mode(void) { Iso15693InitReader(); set_tracing(false); + + picopass_hdr *hdr = (picopass_hdr *)card_data; + // select tag. uint32_t eof_time = 0; - bool res = select_iclass_tag(card_data, auth.use_credit_key, &eof_time); + bool res = select_iclass_tag(hdr, auth.use_credit_key, &eof_time); if (res == false) { switch_off(); continue; } - picopass_hdr *hdr = (picopass_hdr *)card_data; // sanity check of CSN. if (hdr->csn[7] != 0xE0 && hdr->csn[6] != 0x12) { switch_off(); @@ -366,7 +368,7 @@ static int reader_dump_mode(void) { auth.use_credit_key = true; memcpy(auth.key, aa2_key, sizeof(auth.key)); - res = select_iclass_tag(card_data, auth.use_credit_key, &eof_time); + res = select_iclass_tag(hdr, auth.use_credit_key, &eof_time); if (res) { // sanity check of CSN. diff --git a/armsrc/appmain.c b/armsrc/appmain.c index 0b274f654..6b40a3909 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -1553,18 +1553,8 @@ static void PacketReceived(PacketCommandNG *packet) { iClass_Dump(packet->data.asBytes); break; } - case CMD_HF_ICLASS_CLONE: { - struct p { - uint8_t startblock; - uint8_t endblock; - uint8_t data[]; - } PACKED; - struct p *payload = (struct p *)packet->data.asBytes; - iClass_Clone(payload->startblock, payload->endblock, payload->data); - break; - } case CMD_HF_ICLASS_RESTORE: { - iClass_Restore(packet->data.asBytes); + iClass_Restore( (iclass_restore_req_t *)packet->data.asBytes); break; } #endif diff --git a/armsrc/iclass.c b/armsrc/iclass.c index 00e88f405..36ba0706b 100644 --- a/armsrc/iclass.c +++ b/armsrc/iclass.c @@ -1276,7 +1276,7 @@ static bool iclass_send_cmd_with_retries(uint8_t *cmd, size_t cmdsize, uint8_t * * @return false = fail * true = Got all. */ -static bool select_iclass_tag_ex(uint8_t *card_data, bool use_credit_key, uint32_t *eof_time, uint8_t *status) { +static bool select_iclass_tag_ex(picopass_hdr *hdr, bool use_credit_key, uint32_t *eof_time, uint8_t *status) { static uint8_t act_all[] = { ICLASS_CMD_ACTALL }; static uint8_t identify[] = { ICLASS_CMD_READ_OR_IDENTIFY, 0x00, 0x73, 0x33 }; @@ -1286,8 +1286,6 @@ static bool select_iclass_tag_ex(uint8_t *card_data, bool use_credit_key, uint32 uint8_t read_check_cc[] = { 0x80 | ICLASS_CMD_READCHECK, 0x02 }; uint8_t resp[ICLASS_BUFFER_SIZE] = {0}; - picopass_hdr *hdr = (picopass_hdr *)card_data; - // Bit 4: K.If this bit equals to one, the READCHECK will use the Credit Key (Kc); if equals to zero, Debit Key (Kd) will be used // bit 7: parity. if (use_credit_key) @@ -1369,6 +1367,8 @@ static bool select_iclass_tag_ex(uint8_t *card_data, bool use_credit_key, uint32 *status |= FLAG_ICLASS_CC; } else { + + // on NON_SECURE_PAGEMODE cards, AIA is on block2.. // read App Issuer Area block 2 read_aia[1] = 0x02; @@ -1385,23 +1385,23 @@ static bool select_iclass_tag_ex(uint8_t *card_data, bool use_credit_key, uint32 if (status) { *status |= FLAG_ICLASS_AIA; - memcpy(card_data + (8 * 2), resp, 8); + memcpy(hdr->epurse, resp, sizeof(hdr->epurse)); } } return true; } -bool select_iclass_tag(uint8_t *card_data, bool use_credit_key, uint32_t *eof_time) { +bool select_iclass_tag(picopass_hdr *hdr, bool use_credit_key, uint32_t *eof_time) { uint8_t result = 0; - return select_iclass_tag_ex(card_data, use_credit_key, eof_time, &result); + return select_iclass_tag_ex(hdr, use_credit_key, eof_time, &result); } // Reader iClass Anticollission // turn off afterwards void ReaderIClass(uint8_t flags) { - uint8_t card_data[6 * 8] = {0xFF}; + picopass_hdr hdr = {0}; // uint8_t last_csn[8] = {0, 0, 0, 0, 0, 0, 0, 0}; uint8_t resp[ICLASS_BUFFER_SIZE] = {0}; memset(resp, 0xFF, sizeof(resp)); @@ -1419,14 +1419,13 @@ void ReaderIClass(uint8_t flags) { uint8_t result_status = 0; uint32_t eof_time = 0; - bool status = select_iclass_tag_ex(card_data, use_credit_key, &eof_time, &result_status); + bool status = select_iclass_tag_ex(&hdr, use_credit_key, &eof_time, &result_status); if (status == false) { - reply_mix(CMD_ACK, 0xFF, 0, 0, card_data, 0); + reply_mix(CMD_ACK, 0xFF, 0, 0, NULL, 0); switch_off(); return; } - // Page mapping for secure mode // 0 : CSN // 1 : Configuration @@ -1444,7 +1443,7 @@ void ReaderIClass(uint8_t flags) { // with 0xFF:s in block 3 and 4. LED_B_ON(); - reply_mix(CMD_ACK, result_status, 0, 0, card_data, sizeof(card_data)); + reply_mix(CMD_ACK, result_status, 0, 0, (uint8_t*)&hdr, sizeof(hdr)); //Send back to client, but don't bother if we already sent this - // only useful if looping in arm (not try_once && not abort_after_read) @@ -1543,7 +1542,7 @@ void iClass_Authentication_fast(uint64_t arg0, uint64_t arg1, uint8_t *datain) { readcheck_cc[0] = 0x10 | ICLASS_CMD_READCHECK; // select card / e-purse - uint8_t card_data[6 * 8] = {0}; + picopass_hdr hdr = {0}; iclass_premac_t *keys = (iclass_premac_t *)datain; @@ -1557,7 +1556,7 @@ void iClass_Authentication_fast(uint64_t arg0, uint64_t arg1, uint8_t *datain) { uint32_t start_time = 0, eof_time = 0; - if (select_iclass_tag(card_data, use_credit_key, &eof_time) == false) + if (select_iclass_tag(&hdr, use_credit_key, &eof_time) == false) goto out; start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; @@ -1634,7 +1633,7 @@ void iClass_ReadBlock(uint8_t *msg) { // select tag. uint32_t eof_time = 0; picopass_hdr hdr = {0}; - bool res = select_iclass_tag((uint8_t *)&hdr, payload->use_credit_key, &eof_time); + bool res = select_iclass_tag(&hdr, payload->use_credit_key, &eof_time); if (res == false) { if (payload->send_reply) { response.isOK = res; @@ -1707,7 +1706,7 @@ void iClass_Dump(uint8_t *msg) { // select tag. uint32_t eof_time = 0; picopass_hdr hdr = {0}; - bool res = select_iclass_tag((uint8_t *)&hdr, req->use_credit_key, &eof_time); + bool res = select_iclass_tag(&hdr, req->use_credit_key, &eof_time); if (res == false) { if (req->send_reply) { reply_ng(CMD_HF_ICLASS_DUMP, PM3_ETIMEOUT, NULL, 0); @@ -1777,10 +1776,12 @@ void iClass_Dump(uint8_t *msg) { BigBuf_free(); } -static bool iclass_writeblock_ext(uint8_t blockno, uint8_t *data) { +static bool iclass_writeblock_ext(uint8_t blockno, uint8_t *data, uint8_t *mac) { + // write command: cmd, 1 blockno, 8 data, 4 mac uint8_t write[16] = { 0x80 | ICLASS_CMD_UPDATE, blockno }; - memcpy(write + 2, data, 12); // data + mac + memcpy(write + 2, data, 8); + memcpy(write + 10, mac, 4); AddCrc(write + 1, 13); uint8_t resp[10] = {0}; @@ -1825,7 +1826,7 @@ void iClass_WriteBlock(uint8_t *msg) { // select tag. uint32_t eof_time = 0; picopass_hdr hdr = {0}; - bool res = select_iclass_tag((uint8_t *)&hdr, payload->req.use_credit_key, &eof_time); + bool res = select_iclass_tag(&hdr, payload->req.use_credit_key, &eof_time); if (res == false) { goto out; } @@ -1924,29 +1925,75 @@ out: reply_ng(CMD_HF_ICLASS_WRITEBL, PM3_SUCCESS, (uint8_t *)&res, sizeof(uint8_t)); } -// turn off afterwards -void iClass_Clone(uint8_t startblock, uint8_t endblock, uint8_t *data) { -} +void iClass_Restore(iclass_restore_req_t *msg) { -void iClass_Restore(uint8_t *msg) { + // sanitation + if (msg == NULL) { + reply_ng(CMD_HF_ICLASS_RESTORE, PM3_ESOFT, NULL, 0); + return; + } - iclass_restore_req_t *cmd = (iclass_restore_req_t *)msg; -// iclass_auth_req_t *req = &cmd->req; + if (msg->item_cnt == 0) { + if (msg->req.send_reply) { + reply_ng(CMD_HF_ICLASS_RESTORE, PM3_ESOFT, NULL, 0); + } + return; + } LED_A_ON(); - uint16_t written = 0; - uint16_t total_blocks = (cmd->end_block - cmd->start_block) + 1; - for (uint8_t b = cmd->start_block; b < total_blocks; b++) { + Iso15693InitReader(); - if (iclass_writeblock_ext(b, cmd->data + ((b - cmd->start_block) * 12))) { - Dbprintf("Write block [%02x] successful", b); - written++; - } else { - Dbprintf("Write block [%02x] failed", b); + uint16_t written = 0; + uint32_t eof_time = 0; + picopass_hdr hdr = {0}; + + // select + bool res = select_iclass_tag(&hdr, msg->req.use_credit_key, &eof_time); + if (res == false) { + goto out; + } + + // authenticate + uint8_t mac[4] = {0}; + uint32_t start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + + // authenticate + if (msg->req.do_auth) { + res = authenticate_iclass_tag(&msg->req, &hdr, &start_time, &eof_time, mac); + if (res == false) { + goto out; } } + // main loop + for (uint8_t i = 0; i < msg->item_cnt; i++) { + + iclass_restore_item_t item = msg->blocks[i]; + + // calc new mac for data, using 1b blockno, 8b data, + uint8_t wb[9] = {0}; + wb[0] = item.blockno; + memcpy(wb + 1, item.data, 8); + + if (msg->req.use_credit_key) + doMAC_N(wb, sizeof(wb), hdr.key_c, mac); + else + doMAC_N(wb, sizeof(wb), hdr.key_d, mac); + + // data + mac + if (iclass_writeblock_ext(item.blockno, item.data, mac)) { + Dbprintf("Write block [%02x] " _GREEN_("successful"), item.blockno); + written++; + } else { + Dbprintf("Write block [%02x] " _RED_("failed"), item.blockno); + } + } + +out: + switch_off(); - uint8_t isOK = (written == total_blocks) ? 1 : 0; - reply_ng(CMD_HF_ICLASS_CLONE, PM3_SUCCESS, (uint8_t *)&isOK, sizeof(uint8_t)); + if (msg->req.send_reply) { + int isOK = (written == msg->item_cnt) ? PM3_SUCCESS : PM3_ESOFT; + reply_ng(CMD_HF_ICLASS_RESTORE, isOK, NULL, 0); + } } diff --git a/armsrc/iclass.h b/armsrc/iclass.h index ec9ff7b15..1895ebbf6 100644 --- a/armsrc/iclass.h +++ b/armsrc/iclass.h @@ -21,8 +21,7 @@ void ReaderIClass(uint8_t arg0); void iClass_WriteBlock(uint8_t *msg); void iClass_Dump(uint8_t *msg); -void iClass_Restore(uint8_t *msg); -void iClass_Clone(uint8_t startblock, uint8_t endblock, uint8_t *data); +void iClass_Restore(iclass_restore_req_t *msg); int do_iclass_simulation_nonsec(void); int do_iclass_simulation(int simulationMode, uint8_t *reader_mac_buf); @@ -36,6 +35,6 @@ bool iclass_auth(iclass_auth_req_t *payload, uint8_t *out); void iClass_ReadBlock(uint8_t *msg); bool iclass_read_block(uint16_t blockno, uint8_t *data, uint32_t *start_time, uint32_t *eof_time); -bool select_iclass_tag(uint8_t *card_data, bool use_credit_key, uint32_t *eof_time); +bool select_iclass_tag(picopass_hdr *hdr, bool use_credit_key, uint32_t *eof_time); bool authenticate_iclass_tag(iclass_auth_req_t *payload, picopass_hdr *hdr, uint32_t *start_time, uint32_t *eof_time, uint8_t *mac_out); #endif diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index bf40e2d1e..57a231e2e 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -1431,13 +1431,6 @@ static int CmdHFiClassEncryptBlk(const char *Cmd) { return PM3_SUCCESS; } -static void calc_wb_mac(uint8_t blockno, uint8_t *data, uint8_t *div_key, uint8_t *MAC) { - uint8_t wb[9]; - wb[0] = blockno; - memcpy(wb + 1, data, 8); - doMAC_N(wb, sizeof(wb), div_key, MAC); -} - static bool select_only(uint8_t *CSN, uint8_t *CCNR, bool verbose) { uint8_t flags = (FLAG_ICLASS_READER_INIT | FLAG_ICLASS_READER_CLEARTRACE); @@ -1475,47 +1468,6 @@ static bool select_only(uint8_t *CSN, uint8_t *CCNR, bool verbose) { return true; } -static bool select_and_auth(uint8_t *KEY, uint8_t *MAC, uint8_t *div_key, bool use_credit_key, bool elite, bool rawkey, bool verbose) { - - iclass_auth_req_t payload = { - .use_raw = rawkey, - .use_elite = elite, - .use_credit_key = use_credit_key - }; - memcpy(payload.key, KEY, 8); - - SendCommandNG(CMD_HF_ICLASS_AUTH, (uint8_t *)&payload, sizeof(payload)); - PacketResponseNG resp; - clearCommandBuffer(); - if (WaitForResponseTimeout(CMD_HF_ICLASS_AUTH, &resp, 2000) == 0) { - if (verbose) PrintAndLogEx(WARNING, "Command execute timeout"); - return false; - } - - if (resp.status != PM3_SUCCESS) { - if (verbose) PrintAndLogEx(ERR, "failed to communicate with card"); - return false; - } - - iclass_readblock_resp_t *packet = (iclass_readblock_resp_t *)resp.data.asBytes; - - if (packet->isOK == 0) { - if (verbose) PrintAndLogEx(FAILED, "authentication error"); - return false; - } - - if (div_key) - memcpy(div_key, packet->div_key, sizeof(packet->div_key)); - - if (MAC) - memcpy(MAC, packet->mac, sizeof(packet->mac)); - - if (verbose) - PrintAndLogEx(SUCCESS, "authing with %s: %s", rawkey ? "raw key" : "diversified key", sprint_hex(div_key, 8)); - - return true; -} - static int CmdHFiClassDump(const char *Cmd) { uint8_t KEY[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; @@ -2026,11 +1978,6 @@ static int CmdHFiClass_WriteBlock(const char *Cmd) { return isok; } -/* -static int CmdHFiClassClone(const char *Cmd) { - return PM3_SUCCESS; -} -*/ static int CmdHFiClassRestore(const char *Cmd) { char filename[FILE_PATH_SIZE] = { 0x00 }; char tempStr[50] = {0}; @@ -2082,7 +2029,7 @@ static int CmdHFiClassRestore(const char *Cmd) { } else if (dataLen == 1) { keyNbr = param_get8(Cmd, cmdp + 1); if (keyNbr < ICLASS_KEYS_MAX) { - PrintAndLogEx(SUCCESS, "Using key[%d] %s", keyNbr, sprint_hex(iClass_Key_Table[keyNbr], 8)); + PrintAndLogEx(SUCCESS, "Using key[%d] " _GREEN_("%s"), keyNbr, sprint_hex(iClass_Key_Table[keyNbr], 8)); memcpy(KEY, iClass_Key_Table[keyNbr], 8); } else { PrintAndLogEx(WARNING, "\nERROR: Credit KeyNbr is invalid\n"); @@ -2119,14 +2066,19 @@ static int CmdHFiClassRestore(const char *Cmd) { if (errors || cmdp < 8) return usage_hf_iclass_restore(); + if (rawkey + elite > 1) { + PrintAndLogEx(FAILED, "Can not use both 'e', 'r'"); + return PM3_EINVARG; + } + if (startblock < 5) { PrintAndLogEx(WARNING, "you cannot write key blocks this way. yet... make your start block > 4"); return PM3_EINVARG; } - int total_bytes = (((endblock - startblock) + 1) * 12); + uint32_t payload_size = sizeof(iclass_restore_req_t) + (sizeof(iclass_restore_item_t) * (endblock - startblock + 1)); - if (total_bytes > PM3_CMD_DATA_SIZE - 2) { + if (payload_size > PM3_CMD_DATA_SIZE) { PrintAndLogEx(NORMAL, "Trying to write too many blocks at once. Max: %d", PM3_CMD_DATA_SIZE / 8); return PM3_EINVARG; } @@ -2144,93 +2096,68 @@ static int CmdHFiClassRestore(const char *Cmd) { return PM3_EFILE; } - if (bytes_read < sizeof(iclass_block_t) * (endblock - startblock + 1)) { - PrintAndLogEx(ERR, "file wrong size"); + if (bytes_read < ((endblock - startblock + 1) * 8 )) { + PrintAndLogEx(ERR, "file is smaller than your suggested block range ( " _RED_("0x%02x..0x%02x")" )", + startblock, endblock + ); free(dump); return PM3_EFILE; } + iclass_restore_req_t *payload = calloc(1, payload_size); + payload->req.use_raw = rawkey, + payload->req.use_elite = elite, + payload->req.use_credit_key = use_credit_key, + payload->req.use_replay = false, + payload->req.blockno = startblock, + payload->req.send_reply = true, + payload->req.do_auth = true, + memcpy(payload->req.key, KEY, 8); + + payload->item_cnt = (endblock - startblock + 1); + // read data from file from block 6 --- 19 // we will use this struct [data 8 bytes][MAC 4 bytes] for each block calculate all mac number for each data // then copy to usbcommand->asbytes; // max is 32 - 6 = 28 block. 28 x 12 bytes gives 336 bytes - iclass_block_t tag_data[PM3_CMD_DATA_SIZE / 12]; - memcpy(tag_data, dump + startblock * 8, sizeof(iclass_block_t) * (endblock - startblock + 1)); + for (uint8_t i = 0; i < payload->item_cnt; i++) { + payload->blocks[i].blockno = startblock + i; + memcpy(payload->blocks[i].data, dump + (startblock * 8) + (i * 8) , sizeof(payload->blocks[i].data)); + } free(dump); - uint8_t MAC[4] = {0x00, 0x00, 0x00, 0x00}; - uint8_t div_key[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - - int i; - int numberAuthRetries = ICLASS_AUTH_RETRY; - do { - if (select_and_auth(KEY, MAC, div_key, use_credit_key, elite, rawkey, verbose)) - break; - } while (numberAuthRetries--); - - if (numberAuthRetries <= 0) { - PrintAndLogEx(ERR, "failed to authenticate"); - DropField(); - return PM3_ESOFT; - } - - uint8_t data[total_bytes]; - - // calculate all mac for every the block we will write - for (i = startblock; i <= endblock; i++) { - - calc_wb_mac(i, tag_data[i - startblock].d, div_key, MAC); - // usb command d start pointer = d + (i - 6) * 12 - // memcpy(pointer,tag_data[i - 6],8) 8 bytes - // memcpy(pointer + 8,mac,sizoof(mac) 4 bytes; - // next one - uint8_t *ptr = data + (i - startblock) * 12; - memcpy(ptr, &(tag_data[i - startblock].d[0]), 8); - memcpy(ptr + 8, MAC, 4); - } - if (verbose) { - PrintAndLogEx(INFO, "------+--------------------------+-------------"); - PrintAndLogEx(INFO, "block | data | mac"); - PrintAndLogEx(INFO, "------+--------------------------+-------------"); - uint8_t p[12]; - for (i = 0; i <= endblock - startblock; i++) { - memcpy(p, data + (i * 12), 12); - char *s = calloc(70, sizeof(uint8_t)); - snprintf(s, 70, "| %s ", sprint_hex(p, 8)); - snprintf(s + strlen(s), 70 - strlen(s), "| %s", sprint_hex(p + 8, 4)); - PrintAndLogEx(NORMAL, " %02X %s", i + startblock, s); - free(s); + PrintAndLogEx(INFO, "Preparing to restore block range 0x02x..0x%02x", startblock, endblock); + + PrintAndLogEx(INFO, "------+----------------------"); + PrintAndLogEx(INFO, "block | data"); + PrintAndLogEx(INFO, "------+----------------------"); + + for (uint8_t i = 0; i < payload->item_cnt; i++) { + iclass_restore_item_t item = payload->blocks[i]; + PrintAndLogEx(INFO, " %02X | %s", item.blockno, sprint_hex_inrow(item.data, sizeof(item.data))); } } - struct p { - uint8_t startblock; - uint8_t endblock; - uint8_t data[PM3_CMD_DATA_SIZE - 2]; - } PACKED payload; - - payload.startblock = startblock; - payload.endblock = endblock; - memcpy(payload.data, data, total_bytes); + PrintAndLogEx(INFO, "restore started..."); PacketResponseNG resp; clearCommandBuffer(); - SendCommandNG(CMD_HF_ICLASS_CLONE, (uint8_t *)&payload, total_bytes + 2); + SendCommandNG(CMD_HF_ICLASS_RESTORE, (uint8_t *)payload, payload_size); - if (WaitForResponseTimeout(CMD_HF_ICLASS_CLONE, &resp, 2000) == 0) { + if (WaitForResponseTimeout(CMD_HF_ICLASS_RESTORE, &resp, 2500) == 0) { PrintAndLogEx(WARNING, "command execute timeout"); DropField(); return PM3_ETIMEOUT; } if (resp.status == PM3_SUCCESS) { - if (resp.data.asBytes[0] == 1) - PrintAndLogEx(SUCCESS, "Restore successful"); - else - PrintAndLogEx(WARNING, "Restore failed"); + PrintAndLogEx(SUCCESS, "iCLASS restore " _GREEN_("successful")); + PrintAndLogEx(HINT, "Try `" _YELLOW_("hf iclass rdbl ") "` to verify data on card"); + } else { + PrintAndLogEx(WARNING, "iCLASS restore " _RED_("failed")); } return resp.status; } diff --git a/include/pm3_cmd.h b/include/pm3_cmd.h index 141f51621..a473eb7f3 100644 --- a/include/pm3_cmd.h +++ b/include/pm3_cmd.h @@ -334,11 +334,15 @@ typedef struct { } PACKED iclass_writeblock_req_t; // iCLASS dump data structure +typedef struct { + uint8_t blockno; + uint8_t data[8]; +} PACKED iclass_restore_item_t; + typedef struct { iclass_auth_req_t req; - uint8_t start_block; - uint8_t end_block; - uint8_t data[]; + uint8_t item_cnt; + iclass_restore_item_t blocks[]; } PACKED iclass_restore_req_t; @@ -351,7 +355,7 @@ typedef struct { uint8_t mem_config; //[13] uint8_t eas; //[14] uint8_t fuses; //[15] -} picopass_conf_block_t; +} PACKED picopass_conf_block_t; // iCLASS secure mode memory mapping typedef struct { @@ -361,14 +365,14 @@ typedef struct { uint8_t key_d[8]; uint8_t key_c[8]; uint8_t app_issuer_area[8]; -} picopass_hdr; +} PACKED picopass_hdr; // iCLASS non-secure mode memory mapping typedef struct { uint8_t csn[8]; picopass_conf_block_t conf; uint8_t app_issuer_area[8]; -} picopass_ns_hdr; +} PACKED picopass_ns_hdr; // For the bootloader @@ -560,7 +564,6 @@ typedef struct { // iCLASS / Picopass #define CMD_HF_ICLASS_READCHECK 0x038F -#define CMD_HF_ICLASS_CLONE 0x0390 #define CMD_HF_ICLASS_DUMP 0x0391 #define CMD_HF_ICLASS_SNIFF 0x0392 #define CMD_HF_ICLASS_SIMULATE 0x0393 From ba8aa6f0abe7605fcec993df853a88acfee5f255 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Thu, 15 Oct 2020 19:29:54 +0200 Subject: [PATCH 30/58] lf em stuff --- client/src/cmdhfmfp.c | 28 ++-- client/src/cmdhw.c | 27 +++ client/src/cmdhw.h | 2 + client/src/cmdlfem4x.c | 367 ++++++++++++++++++++++++++++++++++++++++- include/pm3_cmd.h | 6 + 5 files changed, 416 insertions(+), 14 deletions(-) diff --git a/client/src/cmdhfmfp.c b/client/src/cmdhfmfp.c index e72799bc4..7d9c077fb 100644 --- a/client/src/cmdhfmfp.c +++ b/client/src/cmdhfmfp.c @@ -233,6 +233,7 @@ static int plus_print_version(uint8_t *version) { PrintAndLogEx(SUCCESS, " Production date: week " _GREEN_("%02x") " / " _GREEN_("20%02x"), version[7 + 7 + 7 + 5], version[7 + 7 + 7 + 5 + 1]); PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "--- " _CYAN_("Hardware Information")); + PrintAndLogEx(INFO, " Raw : %s", sprint_hex(version, 7)); PrintAndLogEx(INFO, " Vendor Id: " _YELLOW_("%s"), getTagInfo(version[0])); PrintAndLogEx(INFO, " Type: %s", getTypeStr(version[1])); PrintAndLogEx(INFO, " Subtype: " _YELLOW_("0x%02X"), version[2]); @@ -241,6 +242,7 @@ static int plus_print_version(uint8_t *version) { PrintAndLogEx(INFO, " Protocol: %s", getProtocolStr(version[6], true)); PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "--- " _CYAN_("Software Information")); + PrintAndLogEx(INFO, " Raw : %s", sprint_hex(version + 7, 6)); PrintAndLogEx(INFO, " Vendor Id: " _YELLOW_("%s"), getTagInfo(version[7])); PrintAndLogEx(INFO, " Type: %s", getTypeStr(version[8])); PrintAndLogEx(INFO, " Subtype: " _YELLOW_("0x%02X"), version[9]); @@ -265,13 +267,21 @@ static int get_plus_version(uint8_t *version, int *version_len) { static int CmdHFMFPInfo(const char *Cmd) { - if (Cmd && strlen(Cmd) > 0) - PrintAndLogEx(WARNING, "command don't have any parameters.\n"); - PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "--- " _CYAN_("Tag Information") " ---------------------------"); PrintAndLogEx(INFO, "-------------------------------------------------------------"); + // Mifare Plus info + SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT, 0, 0, NULL, 0); + PacketResponseNG resp; + WaitForResponse(CMD_ACK, &resp); + + iso14a_card_select_t card; + memcpy(&card, (iso14a_card_select_t *)resp.data.asBytes, sizeof(iso14a_card_select_t)); + + uint64_t select_status = resp.oldarg[0]; // 0: couldn't read, 1: OK, with ATS, 2: OK, no ATS, 3: proprietary Anticollision + + bool supportVersion = false; bool supportSignature = false; @@ -284,17 +294,11 @@ static int CmdHFMFPInfo(const char *Cmd) { } else { // info about 14a part infoHF14A(false, false, false); + + // Historical bytes. + } - // Mifare Plus info - SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT, 0, 0, NULL, 0); - PacketResponseNG resp; - WaitForResponse(CMD_ACK, &resp); - - iso14a_card_select_t card; - memcpy(&card, (iso14a_card_select_t *)resp.data.asBytes, sizeof(iso14a_card_select_t)); - - uint64_t select_status = resp.oldarg[0]; // 0: couldn't read, 1: OK, with ATS, 2: OK, no ATS, 3: proprietary Anticollision // Signature originality check uint8_t signature[56] = {0}; diff --git a/client/src/cmdhw.c b/client/src/cmdhw.c index 68b5fdf40..22da51558 100644 --- a/client/src/cmdhw.c +++ b/client/src/cmdhw.c @@ -22,6 +22,7 @@ #include "cmdhw.h" #include "cmddata.h" #include "commonutil.h" +#include "pm3_cmd.h" static int CmdHelp(const char *Cmd); @@ -516,6 +517,32 @@ static int CmdStatus(const char *Cmd) { return PM3_SUCCESS; } +int handle_tearoff(tearoff_params_t *params, bool verbose) { + + if (params == NULL) + return PM3_EINVARG; + + clearCommandBuffer(); + SendCommandNG(CMD_SET_TEAROFF, (uint8_t *)params, sizeof(tearoff_params_t)); + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_SET_TEAROFF, &resp, 500) == false) { + PrintAndLogEx(WARNING, "Tear-off command timeout."); + return PM3_ETIMEOUT; + } + + if (resp.status == PM3_SUCCESS) { + if (params->delay_us > 0 && verbose) + PrintAndLogEx(INFO, "Tear-off hook configured with delay of " _GREEN_("%i us"), params->delay_us); + + if (params->on && verbose) + PrintAndLogEx(INFO, "Tear-off hook " _GREEN_("enabled")); + + if (params->off && verbose) + PrintAndLogEx(INFO, "Tear-off hook " _RED_("disabled")); + } + return resp.status; +} + static int CmdTearoff(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hw tearoff", diff --git a/client/src/cmdhw.h b/client/src/cmdhw.h index bf40b70a8..4b48eb840 100644 --- a/client/src/cmdhw.h +++ b/client/src/cmdhw.h @@ -12,9 +12,11 @@ #define CMDHW_H__ #include "common.h" +#include "pm3_cmd.h" int CmdHW(const char *Cmd); +int handle_tearoff(tearoff_params_t *params, bool verbose); void pm3_version(bool verbose, bool oneliner); #endif diff --git a/client/src/cmdlfem4x.c b/client/src/cmdlfem4x.c index 00019a569..7ffbc4824 100644 --- a/client/src/cmdlfem4x.c +++ b/client/src/cmdlfem4x.c @@ -32,6 +32,7 @@ #include "lfdemod.h" #include "generator.h" #include "cliparser.h" +#include "cmdhw.h" static uint64_t g_em410xid = 0; @@ -715,7 +716,7 @@ static bool EM_ColParityTest(uint8_t *bs, size_t size, uint8_t rows, uint8_t col static bool downloadSamplesEM(void) { // 8 bit preamble + 32 bit word response (max clock (128) * 40bits = 5120 samples) - uint8_t got[6000]; + uint8_t got[5500]; if (!GetFromDevice(BIG_BUF, got, sizeof(got), 0, NULL, 0, NULL, 2500, false)) { PrintAndLogEx(WARNING, "(downloadSamplesEM) command execution time out"); return false; @@ -1413,10 +1414,11 @@ static void printEM4x05info(uint32_t block0, uint32_t serial) { // 7,8, rfu // 9 - 18 customer code // 19, rfu - + 98765432109876543210 001000000000 // 00100000000001111000 + xxx---- // 1100 // 011 // 00100000000 @@ -1643,6 +1645,366 @@ static int CmdEM4x05Chk(const char *Cmd) { return PM3_SUCCESS; } + +static int unlock_write_protect(bool use_pwd, uint32_t pwd, uint32_t data, bool verbose) { + + struct { + uint32_t password; + uint32_t data; + uint8_t usepwd; + } PACKED payload; + + payload.password = pwd; + payload.data = data; + payload.usepwd = use_pwd; + + clearCommandBuffer(); + SendCommandNG(CMD_LF_EM4X_PROTECTWORD, (uint8_t *)&payload, sizeof(payload)); + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_LF_EM4X_PROTECTWORD, &resp, 2000) == false) { + PrintAndLogEx(ERR, "Error occurred, device did not respond during write operation."); + return PM3_ETIMEOUT; + } + + if (!downloadSamplesEM()) + return PM3_ENODATA; + + uint32_t dummy = 0; + int status = demodEM4x05resp(&dummy, true); + if (status == PM3_SUCCESS && verbose) + PrintAndLogEx(SUCCESS, "Success writing to tag"); + else if (status == PM3_EFAILED) + PrintAndLogEx(ERR, "Tag denied PROTECT operation"); + else + PrintAndLogEx(DEBUG, "No answer from tag"); + + return status; +} +static int unlock_reset(bool use_pwd, uint32_t pwd, uint32_t data) { + PrintAndLogEx(FAILED, "resetting the " _RED_("active") " lock block"); + return unlock_write_protect(use_pwd, pwd, data, false); +} + +static int CmdEM4x05Unlock(const char *Cmd) { + + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf em 4x05_unlock", + "execute tear off against EM4205/4305/4469/4569", + "lf em 4x05_unlock\n" + "lf em 4x05_unlock -s 4100 -e 4100 -> lock on and autotune at 4100us\n" + "lf em 4x05_unlock -n 10 -s 3000 -e 4400 -> scan delays 3000us -> 4400us" + ); + + void *argtable[] = { + arg_param_begin, + arg_u64_0("n", NULL, NULL, "steps to skip"), + arg_u64_0("s", "start", "", "start scan from delay (us)"), + arg_u64_0("e", "end", "", "end scan at delay (us)"), + arg_u64_0("p", "pwd", "", "password (0x00000000)"), + arg_lit0("v", "verbose", "verbose output"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + uint64_t n = arg_get_u64_def(ctx, 1, 10); + uint64_t start = arg_get_u64_def(ctx, 2, 2000); + uint64_t end = arg_get_u64_def(ctx, 3, 6000); + uint64_t inputpwd = arg_get_u64_def(ctx, 4, 0xFFFFFFFFFFFFFFFF); + bool verbose = arg_get_lit(ctx, 5); + CLIParserFree(ctx); + + if (session.pm3_present == false) { + PrintAndLogEx(WARNING, "device offline\n"); + return PM3_ENODATA; + } + + bool use_pwd = false; + uint32_t pwd = 0; + if (inputpwd != 0xFFFFFFFFFFFFFFFF) { + use_pwd = true; + pwd = inputpwd & 0xFFFFFFFF; + } + + uint32_t search_value = 0; + uint32_t write_value = 0; + // + // inital phase + // + // read word 14 + uint32_t init_14 = 0; + int res = EM4x05ReadWord_ext(14, pwd, use_pwd, &init_14); + if (res != PM3_SUCCESS) { + PrintAndLogEx(FAILED, "failed to read word 14\n"); + return PM3_ENODATA; + } + + + // read 15 + uint32_t init_15 = 0; + res = EM4x05ReadWord_ext(15, pwd, use_pwd, &init_15); + if (res != PM3_SUCCESS) { + PrintAndLogEx(FAILED, "failed to read word 15\n"); + return PM3_ENODATA; + } + + +#define UNLOCK_WORD 0x00008000 + if (init_15 == UNLOCK_WORD) { + PrintAndLogEx(SUCCESS, "Tag already fully unlocked, nothing to do"); + return PM3_SUCCESS; + } + +#define ACTIVE_MASK 0x00008000 + if ((init_15 & ACTIVE_MASK) == ACTIVE_MASK) { + search_value = init_15; + } else { + search_value = init_14; + } + + + bool my_auto = false; + if (n == 0) { + my_auto = true; + n = (end - start) / 2; + } else { + if ( start > end ) { + PrintAndLogEx(FAILED, "start delay can\'t be larger than end delay %u vs %u", start, end); + return PM3_EINVARG; + } + } + + // fix at one specific delay + if (start == end) { + n = 0; + } + + PrintAndLogEx(INFO, "--------------- " _CYAN_("EM4x05 tear-off : target PROTECT") " -----------------------\n"); + + PrintAndLogEx(INFO, " Word 14,15 inital [ " _GREEN_("%08"PRIX32) ", " _GREEN_("%08"PRIX32) " ]", init_14, init_15); + + if (use_pwd) { + PrintAndLogEx(INFO, " target password [ " _GREEN_("%08"PRIX32) " ]", pwd); + } + if (my_auto) { + PrintAndLogEx(INFO, " automatic mode [ " _GREEN_("enabled") " ]"); + } + + PrintAndLogEx(INFO, " target stepping [ " _GREEN_("%u") " ]", n); + PrintAndLogEx(INFO, "target delay range [ " _GREEN_("%"PRIu32) " ... " _GREEN_("%"PRIu32) " ]", start, end); + PrintAndLogEx(INFO, " search value [ " _GREEN_("%08"PRIX32) " ]", search_value); + PrintAndLogEx(INFO, " write value [ " _GREEN_("%08"PRIX32) " ]", write_value); + + PrintAndLogEx(INFO, "----------------------------------------------------------------------------\n"); + PrintAndLogEx(INFO, "press " _YELLOW_("'enter'") " to cancel the command"); + + int exit_code = PM3_SUCCESS; + uint32_t word14 = 0, word15 = 0; + uint32_t tries = 0; + uint32_t soon = 0; + uint32_t late = 0; + // + // main loop + // + //uint32_t prev_delay = 0; + + uint64_t t1 = msclock(); + while (start <= end) { + + if (my_auto && n < 1) { + PrintAndLogEx(INFO, "Reached n < 1 => " _YELLOW_("disabling automatic mode")); + end = start; + my_auto = false; + n = 0; + } + + if (my_auto == false) { + start += n; + } + + if (tries >= 5 && n == 0 && soon != late) { + + if (soon > late) { + PrintAndLogEx(INFO, "Tried %d times, soon:%i late:%i => " _CYAN_("adjust +1us >> %u us"), tries, soon, late, start); + start++; + end++; + } else { + PrintAndLogEx(INFO, "Tried %d times, soon:%i late:%i => " _CYAN_("adjust -1us >> %u us"), tries, soon, late, start); + start--; + end--; + } + tries = 0; + soon = 0; + late = 0; + } + + + + if (is_cancelled()) { + exit_code = PM3_EOPABORTED; + break; + } + + /* + if ( start != prev_delay) { + PrintAndLogEx(INFO, "Tear-off delay hook configured => " _GREEN_("%u us"), start); + prev_delay = start; + } + */ + + // set tear off trigger + clearCommandBuffer(); + tearoff_params_t params = { + .delay_us = start, + .on = true, + .off = false + }; + res = handle_tearoff(¶ms, verbose); + if ( res != PM3_SUCCESS ) { + PrintAndLogEx(WARNING, "failed to configure tear off"); + return PM3_EOPABORTED; + } + + // write + res = unlock_write_protect(use_pwd, pwd, write_value, verbose); + + // read after trigger + res = EM4x05ReadWord_ext(14, pwd, use_pwd, &word14); + if (res == PM3_SUCCESS) { + //PrintAndLogEx(INFO, "14 after [ " _GREEN_("%08"PRIX32) " ]", word14); + } else { + continue; + } + + // read after trigger + res = EM4x05ReadWord_ext(15, pwd, use_pwd, &word15); + if (res == PM3_SUCCESS) { + //PrintAndLogEx(INFO, "15 after [ " _GREEN_("%08"PRIX32) " ]", word15); + } else { + continue; + } + + PrintAndLogEx(INFO, "ref:%08X 14:%08X 15:%08X ", search_value, word14, word15); + + if ( word14 == search_value && word15 == 0) { + PrintAndLogEx(INFO, "Status: Nothing happened => " _GREEN_("tearing too soon")); + + if (my_auto) { + start += n; + n /= 2; + PrintAndLogEx(INFO, "Adjusting params: n %i start %i end %i", n, start, end); + } else { + soon++; + } + } else { + + if (word15 == search_value) { + + if (word14 == 0) { + PrintAndLogEx(INFO, "Status: Protect succeeded => " _GREEN_("tearing too late")); + } else { + if ( word14 == search_value) { + PrintAndLogEx(INFO, "Status: 15 ok, 14 not yet erased => " _GREEN_("tearing too late")); + } else { + PrintAndLogEx(INFO, "Status: 15 ok, 14 partially erased => " _GREEN_("tearing too late")); + } + } + unlock_reset(use_pwd, pwd, write_value); + + // read after reset + res = EM4x05ReadWord_ext(14, pwd, use_pwd, &word14); + if (res != PM3_SUCCESS) { + continue; + } + + if (word14 == 0) { + unlock_reset(use_pwd, pwd, write_value); + } + + if (word14 != search_value) { + + // read after reset + res = EM4x05ReadWord_ext(15, pwd, use_pwd, &word15); + if (res == PM3_SUCCESS) { + PrintAndLogEx(INFO, "Status: new definitive value! => " _RED_("SUCCESS:") " 14: " _CYAN_("%08X") " 15: %08X", word14, word15); + break; + } else { + continue; + } + } + if (my_auto) { + end = start; + start -= n; + n /= 2; + PrintAndLogEx(INFO, "Adjusting params: n %i start %i end %i", n, start, end); + } else { + late++; + } + + } else { + + if (( word15 & ACTIVE_MASK) == ACTIVE_MASK) { + + PrintAndLogEx(INFO, "Status: 15 bitflipped and active => " _RED_("SUCCESS?: ") "14: %08X 15: " _CYAN_("%08X"), word14, word15); + PrintAndLogEx(INFO, "Committing results..."); + + unlock_reset(use_pwd, pwd, write_value); + // read after reset + res = EM4x05ReadWord_ext(14, pwd, use_pwd, &word14); + if ( res != PM3_SUCCESS ) { + PrintAndLogEx(WARNING, "failed to read 14"); + return PM3_EOPABORTED; + } + res = EM4x05ReadWord_ext(15, pwd, use_pwd, &word15); + if ( res != PM3_SUCCESS ) { + PrintAndLogEx(WARNING, "failed to read 15"); + return PM3_EOPABORTED; + } + + PrintAndLogEx(INFO, "ref:%08x 14:%08X 15:%08X", search_value, word14, word15); + + if ((word14 & ACTIVE_MASK) == ACTIVE_MASK) { + + if (word14 == word15) { + PrintAndLogEx(INFO, "Status: confirmed => " _RED_("SUCCESS: ") "14: " _CYAN_("%08X") " 15: %08X", word14, word15); + break; + } + + if (word14 != search_value) { + PrintAndLogEx(INFO, "Status: new definitive value! => " _RED_("SUCCESS: ") "14: " _CYAN_("%08X") " 15: %08X", word14, word15); + break; + } + + PrintAndLogEx(INFO, "Status: failed to commit bitflip => " _RED_("FAIL: ") "14: %08X 15: %08X", word14, word15); + } else { + PrintAndLogEx(INFO, "Status: 15 bitflipped but inactive => " _YELLOW_("PROMISING: ") "14: %08X 15: " _CYAN_("%08X"), word14, word15); + + } + + if (my_auto) { + n = 0; + end = start; + } else { + tries = 0; + soon = 0; + late = 0; + } + } + } + } + + if (my_auto == false) { + tries++; + } + } + + PrintAndLogEx(INFO, "----------------------------- " _CYAN_("exit") " ----------------------------------\n"); + t1 = msclock() - t1; + PrintAndLogEx(SUCCESS, "\ntime in unlock " _YELLOW_("%.0f") " seconds\n", (float)t1 / 1000.0); + PrintAndLogEx(INFO, "try " _YELLOW_("`lf em 4x05_dump`")); + PrintAndLogEx(NORMAL, ""); + return exit_code; +} + static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help"}, {"----------", CmdHelp, AlwaysAvailable, "----------------------- " _CYAN_("EM 410x") " -----------------------"}, @@ -1662,6 +2024,7 @@ static command_t CommandTable[] = { {"4x05_info", CmdEM4x05Info, IfPm3Lf, "tag information EM4x05/EM4x69"}, {"4x05_read", CmdEM4x05Read, IfPm3Lf, "read word data from EM4x05/EM4x69"}, {"4x05_write", CmdEM4x05Write, IfPm3Lf, "write word data to EM4x05/EM4x69"}, + {"4x05_unlock", CmdEM4x05Unlock, IfPm3Lf, "execute tear off against EM4x05/EM4x69"}, {"----------", CmdHelp, AlwaysAvailable, "----------------------- " _CYAN_("EM 4x50") " -----------------------"}, {"4x50_dump", CmdEM4x50Dump, IfPm3EM4x50, "dump EM4x50 tag"}, {"4x50_info", CmdEM4x50Info, IfPm3EM4x50, "tag information EM4x50"}, diff --git a/include/pm3_cmd.h b/include/pm3_cmd.h index a473eb7f3..6fca06786 100644 --- a/include/pm3_cmd.h +++ b/include/pm3_cmd.h @@ -375,6 +375,12 @@ typedef struct { } PACKED picopass_ns_hdr; +typedef struct { + uint16_t delay_us; + bool on; + bool off; +} PACKED tearoff_params_t; + // For the bootloader #define CMD_DEVICE_INFO 0x0000 //#define CMD_SETUP_WRITE 0x0001 From 4ba31596d5f432b2b3496efcb3643cdbe5422a8e Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Thu, 15 Oct 2020 19:30:49 +0200 Subject: [PATCH 31/58] adapt some tag identification of ats historical bytes --- client/src/cmdhf14a.c | 44 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 40 insertions(+), 4 deletions(-) diff --git a/client/src/cmdhf14a.c b/client/src/cmdhf14a.c index 2b65be89c..e9ddbf6c5 100644 --- a/client/src/cmdhf14a.c +++ b/client/src/cmdhf14a.c @@ -1831,11 +1831,47 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { if (card.ats[0] > pos && card.ats[0] < card.ats_len - 2) { const char *tip = ""; if (card.ats[0] - pos >= 7) { - if (memcmp(card.ats + pos, "\xC1\x05\x2F\x2F\x01\xBC\xD6", 7) == 0) { - tip = "-> MIFARE Plus X 2K or 4K"; - } else if (memcmp(card.ats + pos, "\xC1\x05\x2F\x2F\x00\x35\xC7", 7) == 0) { - tip = "-> MIFARE Plus S 2K or 4K"; + + if ((card.sak & 0x70) == 0x40) { // and no GetVersion().. + + if (memcmp(card.ats + pos, "\xC1\x05\x2F\x2F\x01\xBC\xD6", 7) == 0) { + tip = "-> MIFARE Plus X 2K/4K (SL3)"; + } else if (memcmp(card.ats + pos, "\xC1\x05\x2F\x2F\x00\x35\xC7", 7) == 0) { + + if ((card.atqa[0] & 0x02) == 0x02) + tip = "-> MIFARE Plus S 2K (SL3)"; + else if ((card.atqa[0] & 0x04) == 0x04) + tip = "-> MIFARE Plus S 4K (SL3)"; + + } else if (memcmp(card.ats + pos, "\xC1\x05\x21\x30\x00\xF6\xD1", 7) == 0) { + tip = "-> MIFARE Plus SE 1K (17pF)"; + } else if (memcmp(card.ats + pos, "\xC1\x05\x21\x30\x10\xF6\xD1", 7) == 0) { + tip = "-> MIFARE Plus SE 1K (70pF)"; + } + + } else { //SAK B4,5,6 + + if ((card.sak & 0x20) == 0x20) { // and no GetVersion().. + + + if (memcmp(card.ats + pos, "\xC1\x05\x2F\x2F\x01\xBC\xD6", 7) == 0) { + tip = "-> MIFARE Plus X 2K (SL1)"; + } else if (memcmp(card.ats + pos, "\xC1\x05\x2F\x2F\x00\x35\xC7", 7) == 0) { + tip = "-> MIFARE Plus S 2K (SL1)"; + } else if (memcmp(card.ats + pos, "\xC1\x05\x21\x30\x00\xF6\xD1", 7) == 0) { + tip = "-> MIFARE Plus SE 1K (17pF)"; + } else if (memcmp(card.ats + pos, "\xC1\x05\x21\x30\x10\xF6\xD1", 7) == 0) { + tip = "-> MIFARE Plus SE 1K (70pF)"; + } + } else { + if (memcmp(card.ats + pos, "\xC1\x05\x2F\x2F\x01\xBC\xD6", 7) == 0) { + tip = "-> MIFARE Plus X 4K (SL1)"; + } else if (memcmp(card.ats + pos, "\xC1\x05\x2F\x2F\x00\x35\xC7", 7) == 0) { + tip = "-> MIFARE Plus S 4K (SL1)"; + } + } } + } PrintAndLogEx(SUCCESS, " - HB : %s%s", sprint_hex(card.ats + pos, card.ats[0] - pos), tip); if (card.ats[pos] == 0xC1) { From 8bb762dc5b067e23854f678b7dfb7cf609ffc7a4 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Thu, 15 Oct 2020 19:31:33 +0200 Subject: [PATCH 32/58] structs must be PACKED --- include/em4x50.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/em4x50.h b/include/em4x50.h index b70072c32..4542e7bec 100644 --- a/include/em4x50.h +++ b/include/em4x50.h @@ -49,7 +49,7 @@ typedef struct { uint8_t addresses[4]; uint8_t address; uint8_t word[4]; -} em4x50_data_t; +} PACKED em4x50_data_t; typedef struct { uint8_t byte[4]; @@ -60,6 +60,6 @@ typedef struct { bool cparity[8]; bool stopparity; bool parity; -} em4x50_word_t; +} PACKED em4x50_word_t; #endif /* EM4X50_H__ */ From df5c635a7d3c999c07f7e81d5bd666aa4f3fed52 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Thu, 15 Oct 2020 19:38:49 +0200 Subject: [PATCH 33/58] hook up 4x50 read for lua --- client/src/scripting.c | 73 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 68 insertions(+), 5 deletions(-) diff --git a/client/src/scripting.c b/client/src/scripting.c index 050b4147b..90e19151c 100644 --- a/client/src/scripting.c +++ b/client/src/scripting.c @@ -37,6 +37,8 @@ #include "cmdlf.h" // lf_config #include "generator.h" #include "cmdlfem4x.h" // read 4305 +#include "cmdlfem4x50.h" // read 4350 +#include "em4x50.h" // 4x50 structs static int returnToLuaWithError(lua_State *L, const char *fmt, ...) { char buffer[200]; @@ -1088,7 +1090,7 @@ static int l_T55xx_detect(lua_State *L) { return 2; } -// +// 4305 static int l_em4x05_read(lua_State *L) { bool use_pwd = false; @@ -1103,12 +1105,12 @@ static int l_em4x05_read(lua_State *L) { sscanf(p_addr, "%u", &addr); // get password - const char *p_pwd = luaL_checklstring(L, 2, &size); - if (size == 0) { + const char *p_pwd = luaL_checkstring(L, 2); + if (p_pwd == NULL || strlen(p_pwd) == 0 ) { use_pwd = false; } else { - if (size != 8) - return returnToLuaWithError(L, "Wrong size of password, got %zu , expected 8", size); + if (strlen(p_pwd) != 8) + return returnToLuaWithError(L, "Wrong size of password, got %zu , expected 8", strlen(p_pwd)); sscanf(p_pwd, "%08x", &password); use_pwd = true; @@ -1128,6 +1130,66 @@ static int l_em4x05_read(lua_State *L) { return 1; } +// 4350 +static int l_em4x50_read(lua_State *L) { + + // get addr + size_t size = 0; + const char *p_addr = luaL_checklstring(L, 1, &size); + uint32_t addr = 0; + sscanf(p_addr, "%u", &addr); + + if (addr > 33) + return returnToLuaWithError(L, "Address out-of-range (0..33) got %zu", addr); + + // setting up structures + em4x50_data_t etd; + memset(&etd, 0x00, sizeof(em4x50_data_t)); + etd.addr_given = true; + etd.address = addr & 0xFF; + etd.newpwd_given = false; + + // get password + const char *p_pwd = luaL_checkstring(L, 2); + if (p_pwd == NULL || strlen(p_pwd) == 0) { + etd.pwd_given = false; + } else { + if (strlen(p_pwd) != 8) + return returnToLuaWithError(L, "Wrong size of password, got %zu , expected 8", strlen(p_pwd)); + + uint32_t pwd = 0; + sscanf(p_pwd, "%08x", &pwd); + + PrintAndLogEx(DEBUG, " Pwd %08X", pwd); + + etd.password[0] = pwd & 0xFF; + etd.password[1] = (pwd >> 8) & 0xFF; + etd.password[2] = (pwd >> 16) & 0xFF; + etd.password[3] = (pwd >> 24) & 0xFF; + etd.pwd_given = true; + } + + PrintAndLogEx(DEBUG, "Addr %u", etd.address); + if (etd.pwd_given) + PrintAndLogEx(DEBUG, " Pwd %08X", etd.password); + + em4x50_word_t words[EM4X50_NO_WORDS]; + + int res = em4x50_read(&etd, words, false); + if (res != PM3_SUCCESS) { + return returnToLuaWithError(L, "Failed to read EM4x50 data"); + } + + uint32_t word = ( + words[etd.address].byte[0] << 24 | + words[etd.address].byte[1] << 16 | + words[etd.address].byte[2] << 8 | + words[etd.address].byte[3] + ); + lua_pushinteger(L, word); + return 1; +} + // static int l_ndefparse(lua_State *L) { @@ -1320,6 +1382,7 @@ int set_pm3_libraries(lua_State *L) { {"ud", l_ud}, {"rem", l_remark}, {"em4x05_read", l_em4x05_read}, + {"em4x50_read", l_em4x50_read}, {NULL, NULL} }; From e4967ac654ebe07b701b086dec49170af0ce8196 Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Thu, 15 Oct 2020 20:06:27 +0200 Subject: [PATCH 34/58] use handle --- client/src/cmdhw.c | 33 ++++----------------------------- 1 file changed, 4 insertions(+), 29 deletions(-) diff --git a/client/src/cmdhw.c b/client/src/cmdhw.c index 22da51558..f2eff6966 100644 --- a/client/src/cmdhw.c +++ b/client/src/cmdhw.c @@ -539,7 +539,8 @@ int handle_tearoff(tearoff_params_t *params, bool verbose) { if (params->off && verbose) PrintAndLogEx(INFO, "Tear-off hook " _RED_("disabled")); - } + } else if (verbose) + PrintAndLogEx(WARNING, "Tear-off command failed."); return resp.status; } @@ -563,11 +564,7 @@ static int CmdTearoff(const char *Cmd) { }; CLIExecWithReturn(ctx, Cmd, argtable, false); - struct { - uint16_t delay_us; - bool on; - bool off; - } PACKED params; + tearoff_params_t params; int delay = arg_get_int_def(ctx, 1, -1); params.on = arg_get_lit(ctx, 2); params.off = arg_get_lit(ctx, 3); @@ -589,29 +586,7 @@ static int CmdTearoff(const char *Cmd) { return PM3_EINVARG; } - clearCommandBuffer(); - SendCommandNG(CMD_SET_TEAROFF, (uint8_t *)¶ms, sizeof(params)); - PacketResponseNG resp; - - if (WaitForResponseTimeout(CMD_SET_TEAROFF, &resp, 500) == false) { - PrintAndLogEx(WARNING, "Tear-off command timeout."); - return PM3_ETIMEOUT; - } - - if (resp.status == PM3_SUCCESS) { - if (params.delay_us > 0) - PrintAndLogEx(INFO, "Tear-off hook configured with delay of " _GREEN_("%i us"), params.delay_us); - if (params.on && silent == false) - PrintAndLogEx(INFO, "Tear-off hook " _GREEN_("enabled")); - if (params.off && silent == false) - PrintAndLogEx(INFO, "Tear-off hook " _RED_("disabled")); - return PM3_SUCCESS; - } - - if (silent == false) - PrintAndLogEx(WARNING, "Tear-off command failed."); - - return resp.status; + return handle_tearoff(¶ms, !silent); } static int CmdTia(const char *Cmd) { From 6872095b978807ff01d610ea4cd178de9f0baf69 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Thu, 15 Oct 2020 20:10:56 +0200 Subject: [PATCH 35/58] qt session management error --- doc/md/Installation_Instructions/Troubleshooting.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/doc/md/Installation_Instructions/Troubleshooting.md b/doc/md/Installation_Instructions/Troubleshooting.md index da41d56bd..17161ed09 100644 --- a/doc/md/Installation_Instructions/Troubleshooting.md +++ b/doc/md/Installation_Instructions/Troubleshooting.md @@ -22,6 +22,7 @@ Always use the latest repository commits from *master* branch. There are always * [Troubles with running the Proxmark3 client](#troubles-with-running-the-proxmark3-client) * [libQt5Core.so.5 not found](#libQt5Coreso5-not-found) * [Target attribute is not supported on this machine](#target-attribute-is-not-supported-on-this-machine) + * [Qt: Session management error:](#qt-session-management-error) ## `pm3` or `pm3-flash*` doesn't see my Proxmark @@ -222,4 +223,16 @@ ticks.h:26:1: error: target attribute is not supported on this machine [-Werror= ^ ``` +## Qt Session management error +If you get the message +``` +Qt: Session management error: None of the authentication protocols specified are supported +``` when running the Proxmark3 client it might be because a a environment variable. + +Solution: +Try running the client without the SESSION_MANAGER environment variable. + +``` +env -u SESSION_MANAGER ./pm3 +``` \ No newline at end of file From 03d994ea2cdaebf0172406cfae067485e3724ed6 Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Thu, 15 Oct 2020 22:01:19 +0200 Subject: [PATCH 36/58] em stuff --- client/luascripts/lf_em_tearoff_protect.lua | 2 +- client/src/cmdlfem4x.c | 123 ++++++++++---------- 2 files changed, 62 insertions(+), 63 deletions(-) diff --git a/client/luascripts/lf_em_tearoff_protect.lua b/client/luascripts/lf_em_tearoff_protect.lua index 49ee99cec..04cb7d0b5 100644 --- a/client/luascripts/lf_em_tearoff_protect.lua +++ b/client/luascripts/lf_em_tearoff_protect.lua @@ -36,7 +36,7 @@ arguments = [[ end ]] -local set_tearoff_delay = 'hw tearoff -s --on --delay %d' +local set_tearoff_delay = 'hw tearoff --on --delay %d' local wr_template = 'lf em 4x05_write %s %s %s' --- diff --git a/client/src/cmdlfem4x.c b/client/src/cmdlfem4x.c index 7ffbc4824..616b4be8f 100644 --- a/client/src/cmdlfem4x.c +++ b/client/src/cmdlfem4x.c @@ -1681,7 +1681,7 @@ static int unlock_write_protect(bool use_pwd, uint32_t pwd, uint32_t data, bool return status; } static int unlock_reset(bool use_pwd, uint32_t pwd, uint32_t data) { - PrintAndLogEx(FAILED, "resetting the " _RED_("active") " lock block"); + PrintAndLogEx(INFO, "resetting the " _RED_("active") " lock block"); return unlock_write_protect(use_pwd, pwd, data, false); } @@ -1697,22 +1697,26 @@ static int CmdEM4x05Unlock(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_u64_0("n", NULL, NULL, "steps to skip"), - arg_u64_0("s", "start", "", "start scan from delay (us)"), - arg_u64_0("e", "end", "", "end scan at delay (us)"), + arg_int0("n", NULL, NULL, "steps to skip"), + arg_int0("s", "start", "", "start scan from delay (us)"), + arg_int0("e", "end", "", "end scan at delay (us)"), arg_u64_0("p", "pwd", "", "password (0x00000000)"), arg_lit0("v", "verbose", "verbose output"), arg_param_end }; - CLIExecWithReturn(ctx, Cmd, argtable, false); - - uint64_t n = arg_get_u64_def(ctx, 1, 10); - uint64_t start = arg_get_u64_def(ctx, 2, 2000); - uint64_t end = arg_get_u64_def(ctx, 3, 6000); + CLIExecWithReturn(ctx, Cmd, argtable, true); + double n = (double)arg_get_int_def(ctx, 1, 0); + double start = (double)arg_get_int_def(ctx, 2, 2000); + double end = (double)arg_get_int_def(ctx, 3, 6000); uint64_t inputpwd = arg_get_u64_def(ctx, 4, 0xFFFFFFFFFFFFFFFF); bool verbose = arg_get_lit(ctx, 5); CLIParserFree(ctx); + if ( start > end ) { + PrintAndLogEx(FAILED, "start delay can\'t be larger than end delay %.0lf vs %.0lf", start, end); + return PM3_EINVARG; + } + if (session.pm3_present == false) { PrintAndLogEx(WARNING, "device offline\n"); return PM3_ENODATA; @@ -1726,7 +1730,7 @@ static int CmdEM4x05Unlock(const char *Cmd) { } uint32_t search_value = 0; - uint32_t write_value = 0; + uint32_t write_value = 0xFFFF0000; // // inital phase // @@ -1747,13 +1751,6 @@ static int CmdEM4x05Unlock(const char *Cmd) { return PM3_ENODATA; } - -#define UNLOCK_WORD 0x00008000 - if (init_15 == UNLOCK_WORD) { - PrintAndLogEx(SUCCESS, "Tag already fully unlocked, nothing to do"); - return PM3_SUCCESS; - } - #define ACTIVE_MASK 0x00008000 if ((init_15 & ACTIVE_MASK) == ACTIVE_MASK) { search_value = init_15; @@ -1761,16 +1758,15 @@ static int CmdEM4x05Unlock(const char *Cmd) { search_value = init_14; } + if (search_value == ACTIVE_MASK) { + PrintAndLogEx(SUCCESS, "Tag already fully unlocked, nothing to do"); + return PM3_SUCCESS; + } bool my_auto = false; if (n == 0) { my_auto = true; n = (end - start) / 2; - } else { - if ( start > end ) { - PrintAndLogEx(FAILED, "start delay can\'t be larger than end delay %u vs %u", start, end); - return PM3_EINVARG; - } } // fix at one specific delay @@ -1780,19 +1776,19 @@ static int CmdEM4x05Unlock(const char *Cmd) { PrintAndLogEx(INFO, "--------------- " _CYAN_("EM4x05 tear-off : target PROTECT") " -----------------------\n"); - PrintAndLogEx(INFO, " Word 14,15 inital [ " _GREEN_("%08"PRIX32) ", " _GREEN_("%08"PRIX32) " ]", init_14, init_15); + PrintAndLogEx(INFO, "initial prot 14&15 [ " _GREEN_("%08X") ", " _GREEN_("%08X") " ]", init_14, init_15); if (use_pwd) { - PrintAndLogEx(INFO, " target password [ " _GREEN_("%08"PRIX32) " ]", pwd); + PrintAndLogEx(INFO, " target password [ " _GREEN_("%08"PRIX64) " ]", pwd); } if (my_auto) { PrintAndLogEx(INFO, " automatic mode [ " _GREEN_("enabled") " ]"); } - PrintAndLogEx(INFO, " target stepping [ " _GREEN_("%u") " ]", n); - PrintAndLogEx(INFO, "target delay range [ " _GREEN_("%"PRIu32) " ... " _GREEN_("%"PRIu32) " ]", start, end); - PrintAndLogEx(INFO, " search value [ " _GREEN_("%08"PRIX32) " ]", search_value); - PrintAndLogEx(INFO, " write value [ " _GREEN_("%08"PRIX32) " ]", write_value); + PrintAndLogEx(INFO, " target stepping [ " _GREEN_("%.0lf") " ]", n); + PrintAndLogEx(INFO, "target delay range [ " _GREEN_("%.0lf") " ... " _GREEN_("%.0lf") " ]", start, end); + PrintAndLogEx(INFO, " search value [ " _GREEN_("%08X") " ]", search_value); + PrintAndLogEx(INFO, " write value [ " _GREEN_("%08X") " ]", write_value); PrintAndLogEx(INFO, "----------------------------------------------------------------------------\n"); PrintAndLogEx(INFO, "press " _YELLOW_("'enter'") " to cancel the command"); @@ -1824,11 +1820,11 @@ static int CmdEM4x05Unlock(const char *Cmd) { if (tries >= 5 && n == 0 && soon != late) { if (soon > late) { - PrintAndLogEx(INFO, "Tried %d times, soon:%i late:%i => " _CYAN_("adjust +1us >> %u us"), tries, soon, late, start); + PrintAndLogEx(INFO, "Tried %d times, soon:%i late:%i => " _CYAN_("adjust +1 us >> %.0lf us"), tries, soon, late, start); start++; end++; } else { - PrintAndLogEx(INFO, "Tried %d times, soon:%i late:%i => " _CYAN_("adjust -1us >> %u us"), tries, soon, late, start); + PrintAndLogEx(INFO, "Tried %d times, soon:%i late:%i => " _CYAN_("adjust -1 us >> %.0lf us"), tries, soon, late, start); start--; end--; } @@ -1846,7 +1842,7 @@ static int CmdEM4x05Unlock(const char *Cmd) { /* if ( start != prev_delay) { - PrintAndLogEx(INFO, "Tear-off delay hook configured => " _GREEN_("%u us"), start); + PrintAndLogEx(INFO, "Tear-off delay hook configured => " _GREEN_("%.0lf us"), start); prev_delay = start; } */ @@ -1870,7 +1866,7 @@ static int CmdEM4x05Unlock(const char *Cmd) { // read after trigger res = EM4x05ReadWord_ext(14, pwd, use_pwd, &word14); if (res == PM3_SUCCESS) { - //PrintAndLogEx(INFO, "14 after [ " _GREEN_("%08"PRIX32) " ]", word14); + //PrintAndLogEx(INFO, "14 after [ " _GREEN_("%08X") " ]", word14); } else { continue; } @@ -1878,20 +1874,21 @@ static int CmdEM4x05Unlock(const char *Cmd) { // read after trigger res = EM4x05ReadWord_ext(15, pwd, use_pwd, &word15); if (res == PM3_SUCCESS) { - //PrintAndLogEx(INFO, "15 after [ " _GREEN_("%08"PRIX32) " ]", word15); + //PrintAndLogEx(INFO, "15 after [ " _GREEN_("%08X") " ]", word15); } else { continue; } - PrintAndLogEx(INFO, "ref:%08X 14:%08X 15:%08X ", search_value, word14, word15); + if (verbose) + PrintAndLogEx(INFO, "ref:%08X 14:%08X 15:%08X ", search_value, word14, word15); if ( word14 == search_value && word15 == 0) { PrintAndLogEx(INFO, "Status: Nothing happened => " _GREEN_("tearing too soon")); if (my_auto) { start += n; + PrintAndLogEx(INFO, " => " _CYAN_("adjust +%.0lf us >> %.0lf us"), n, start); n /= 2; - PrintAndLogEx(INFO, "Adjusting params: n %i start %i end %i", n, start, end); } else { soon++; } @@ -1909,23 +1906,23 @@ static int CmdEM4x05Unlock(const char *Cmd) { } } unlock_reset(use_pwd, pwd, write_value); - + uint32_t word14b = 0, word15b = 0; // read after reset - res = EM4x05ReadWord_ext(14, pwd, use_pwd, &word14); + res = EM4x05ReadWord_ext(14, pwd, use_pwd, &word14b); if (res != PM3_SUCCESS) { continue; } - if (word14 == 0) { + if (word14b == 0) { unlock_reset(use_pwd, pwd, write_value); } - if (word14 != search_value) { + if (word14b != search_value) { // read after reset - res = EM4x05ReadWord_ext(15, pwd, use_pwd, &word15); + res = EM4x05ReadWord_ext(15, pwd, use_pwd, &word15b); if (res == PM3_SUCCESS) { - PrintAndLogEx(INFO, "Status: new definitive value! => " _RED_("SUCCESS:") " 14: " _CYAN_("%08X") " 15: %08X", word14, word15); + PrintAndLogEx(INFO, "Status: new definitive value! => " _RED_("SUCCESS:") " 14: " _CYAN_("%08X") " 15: %08X", word14b, word15b); break; } else { continue; @@ -1934,8 +1931,8 @@ static int CmdEM4x05Unlock(const char *Cmd) { if (my_auto) { end = start; start -= n; + PrintAndLogEx(INFO, " => " _CYAN_("adjust -%.0lf us >> %.0lf us"), n, start); n /= 2; - PrintAndLogEx(INFO, "Adjusting params: n %i start %i end %i", n, start, end); } else { late++; } @@ -1948,46 +1945,48 @@ static int CmdEM4x05Unlock(const char *Cmd) { PrintAndLogEx(INFO, "Committing results..."); unlock_reset(use_pwd, pwd, write_value); + uint32_t word14b = 0, word15b = 0; // read after reset - res = EM4x05ReadWord_ext(14, pwd, use_pwd, &word14); + res = EM4x05ReadWord_ext(14, pwd, use_pwd, &word14b); if ( res != PM3_SUCCESS ) { PrintAndLogEx(WARNING, "failed to read 14"); return PM3_EOPABORTED; } - res = EM4x05ReadWord_ext(15, pwd, use_pwd, &word15); + res = EM4x05ReadWord_ext(15, pwd, use_pwd, &word15b); if ( res != PM3_SUCCESS ) { PrintAndLogEx(WARNING, "failed to read 15"); return PM3_EOPABORTED; } - PrintAndLogEx(INFO, "ref:%08x 14:%08X 15:%08X", search_value, word14, word15); + if (verbose) + PrintAndLogEx(INFO, "ref:%08x 14:%08X 15:%08X", search_value, word14b, word15b); - if ((word14 & ACTIVE_MASK) == ACTIVE_MASK) { + if ((word14b & ACTIVE_MASK) == ACTIVE_MASK) { - if (word14 == word15) { - PrintAndLogEx(INFO, "Status: confirmed => " _RED_("SUCCESS: ") "14: " _CYAN_("%08X") " 15: %08X", word14, word15); + if (word14b == word15) { + PrintAndLogEx(INFO, "Status: confirmed => " _RED_("SUCCESS: ") "14: " _CYAN_("%08X") " 15: %08X", word14b, word15b); break; } - if (word14 != search_value) { - PrintAndLogEx(INFO, "Status: new definitive value! => " _RED_("SUCCESS: ") "14: " _CYAN_("%08X") " 15: %08X", word14, word15); + if (word14b != search_value) { + PrintAndLogEx(INFO, "Status: new definitive value! => " _RED_("SUCCESS: ") "14: " _CYAN_("%08X") " 15: %08X", word14b, word15b); break; } - PrintAndLogEx(INFO, "Status: failed to commit bitflip => " _RED_("FAIL: ") "14: %08X 15: %08X", word14, word15); - } else { - PrintAndLogEx(INFO, "Status: 15 bitflipped but inactive => " _YELLOW_("PROMISING: ") "14: %08X 15: " _CYAN_("%08X"), word14, word15); - + PrintAndLogEx(INFO, "Status: failed to commit bitflip => " _RED_("FAIL: ") "14: %08X 15: %08X", word14b, word15b); } + } else { + PrintAndLogEx(INFO, "Status: 15 bitflipped but inactive => " _YELLOW_("PROMISING: ") "14: %08X 15: " _CYAN_("%08X"), word14, word15); - if (my_auto) { - n = 0; - end = start; - } else { - tries = 0; - soon = 0; - late = 0; - } + } + + if (my_auto) { + n = 0; + end = start; + } else { + tries = 0; + soon = 0; + late = 0; } } } From 63a2d38191c32eac5ae7e7f5bd41dfa2c302e069 Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Thu, 15 Oct 2020 22:05:18 +0200 Subject: [PATCH 37/58] fix debug val --- client/src/cmdlfem4x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/cmdlfem4x.c b/client/src/cmdlfem4x.c index 616b4be8f..b3d436d91 100644 --- a/client/src/cmdlfem4x.c +++ b/client/src/cmdlfem4x.c @@ -1730,7 +1730,7 @@ static int CmdEM4x05Unlock(const char *Cmd) { } uint32_t search_value = 0; - uint32_t write_value = 0xFFFF0000; + uint32_t write_value = 0; // // inital phase // From 17e5e721114edc0620d530a0c0742cb1c4848651 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Thu, 15 Oct 2020 22:42:05 +0200 Subject: [PATCH 38/58] math const --- client/dictionaries/t55xx_default_pwds.dic | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/client/dictionaries/t55xx_default_pwds.dic b/client/dictionaries/t55xx_default_pwds.dic index c35014d79..cd03c7b7a 100644 --- a/client/dictionaries/t55xx_default_pwds.dic +++ b/client/dictionaries/t55xx_default_pwds.dic @@ -123,3 +123,7 @@ b5f44686 # seeds ul-ev1 C6EF3720 # TEA 7854794A # xbox tea constant :) F1EA5EED # burtle +69314718 # ln2 +57721566 # euler constant (dec) +93C467E3 # euler constant (hex) +27182818 # natural log \ No newline at end of file From f39131f5f05745232431ee412b412f4001a7d74c Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Thu, 15 Oct 2020 22:57:57 +0200 Subject: [PATCH 39/58] forgot to read back after reset --- client/luascripts/lf_em_tearoff_protect.lua | 4 ++++ client/src/cmdlfem4x.c | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/client/luascripts/lf_em_tearoff_protect.lua b/client/luascripts/lf_em_tearoff_protect.lua index 04cb7d0b5..7ab6f9314 100644 --- a/client/luascripts/lf_em_tearoff_protect.lua +++ b/client/luascripts/lf_em_tearoff_protect.lua @@ -250,6 +250,10 @@ local function main(args) local wordstr14b = ('%08X'):format(word14b) if (wordstr14b == '00000000') then reset(wr_value, password) + word14b, err14b = core.em4x05_read(14, password) + if err14b then + return oops(err14b) + end end if (wordstr14b ~= rd_value) then local word15b, err15b = core.em4x05_read(15, password) diff --git a/client/src/cmdlfem4x.c b/client/src/cmdlfem4x.c index b3d436d91..a69c7894b 100644 --- a/client/src/cmdlfem4x.c +++ b/client/src/cmdlfem4x.c @@ -1915,6 +1915,10 @@ static int CmdEM4x05Unlock(const char *Cmd) { if (word14b == 0) { unlock_reset(use_pwd, pwd, write_value); + res = EM4x05ReadWord_ext(14, pwd, use_pwd, &word14b); + if (res != PM3_SUCCESS) { + continue; + } } if (word14b != search_value) { From ab2d23268944b31d6ae36f1309bca5219526c80e Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Thu, 15 Oct 2020 23:31:03 +0200 Subject: [PATCH 40/58] em tuning --- client/src/cmdlfem4x.c | 39 ++++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/client/src/cmdlfem4x.c b/client/src/cmdlfem4x.c index a69c7894b..29ab12287 100644 --- a/client/src/cmdlfem4x.c +++ b/client/src/cmdlfem4x.c @@ -1802,7 +1802,7 @@ static int CmdEM4x05Unlock(const char *Cmd) { // main loop // //uint32_t prev_delay = 0; - + bool success = false; uint64_t t1 = msclock(); while (start <= end) { @@ -1868,7 +1868,7 @@ static int CmdEM4x05Unlock(const char *Cmd) { if (res == PM3_SUCCESS) { //PrintAndLogEx(INFO, "14 after [ " _GREEN_("%08X") " ]", word14); } else { - continue; + break; } // read after trigger @@ -1876,7 +1876,7 @@ static int CmdEM4x05Unlock(const char *Cmd) { if (res == PM3_SUCCESS) { //PrintAndLogEx(INFO, "15 after [ " _GREEN_("%08X") " ]", word15); } else { - continue; + break; } if (verbose) @@ -1910,14 +1910,14 @@ static int CmdEM4x05Unlock(const char *Cmd) { // read after reset res = EM4x05ReadWord_ext(14, pwd, use_pwd, &word14b); if (res != PM3_SUCCESS) { - continue; + break; } if (word14b == 0) { unlock_reset(use_pwd, pwd, write_value); res = EM4x05ReadWord_ext(14, pwd, use_pwd, &word14b); if (res != PM3_SUCCESS) { - continue; + break; } } @@ -1927,10 +1927,9 @@ static int CmdEM4x05Unlock(const char *Cmd) { res = EM4x05ReadWord_ext(15, pwd, use_pwd, &word15b); if (res == PM3_SUCCESS) { PrintAndLogEx(INFO, "Status: new definitive value! => " _RED_("SUCCESS:") " 14: " _CYAN_("%08X") " 15: %08X", word14b, word15b); - break; - } else { - continue; + success = true; } + break; } if (my_auto) { end = start; @@ -1969,28 +1968,29 @@ static int CmdEM4x05Unlock(const char *Cmd) { if (word14b == word15) { PrintAndLogEx(INFO, "Status: confirmed => " _RED_("SUCCESS: ") "14: " _CYAN_("%08X") " 15: %08X", word14b, word15b); + success = true; break; } if (word14b != search_value) { PrintAndLogEx(INFO, "Status: new definitive value! => " _RED_("SUCCESS: ") "14: " _CYAN_("%08X") " 15: %08X", word14b, word15b); + success = true; break; } PrintAndLogEx(INFO, "Status: failed to commit bitflip => " _RED_("FAIL: ") "14: %08X 15: %08X", word14b, word15b); } + if (my_auto) { + n = 0; + end = start; + } else { + tries = 0; + soon = 0; + late = 0; + } } else { PrintAndLogEx(INFO, "Status: 15 bitflipped but inactive => " _YELLOW_("PROMISING: ") "14: %08X 15: " _CYAN_("%08X"), word14, word15); - - } - - if (my_auto) { - n = 0; - end = start; - } else { - tries = 0; - soon = 0; - late = 0; + soon ++; } } } @@ -2003,7 +2003,8 @@ static int CmdEM4x05Unlock(const char *Cmd) { PrintAndLogEx(INFO, "----------------------------- " _CYAN_("exit") " ----------------------------------\n"); t1 = msclock() - t1; PrintAndLogEx(SUCCESS, "\ntime in unlock " _YELLOW_("%.0f") " seconds\n", (float)t1 / 1000.0); - PrintAndLogEx(INFO, "try " _YELLOW_("`lf em 4x05_dump`")); + if (success) + PrintAndLogEx(INFO, "try " _YELLOW_("`lf em 4x05_dump`")); PrintAndLogEx(NORMAL, ""); return exit_code; } From c454b1f3e61ae4599c6ff0e3d31a6b8f88bb11ec Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Thu, 15 Oct 2020 23:36:17 +0200 Subject: [PATCH 41/58] sync script --- client/luascripts/lf_em_tearoff_protect.lua | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/client/luascripts/lf_em_tearoff_protect.lua b/client/luascripts/lf_em_tearoff_protect.lua index 7ab6f9314..8d18ec5da 100644 --- a/client/luascripts/lf_em_tearoff_protect.lua +++ b/client/luascripts/lf_em_tearoff_protect.lua @@ -303,17 +303,17 @@ local function main(args) else print(('[=] Status: failed to commit => '..ansicolors.red..'FAIL: '..ansicolors.reset..'14: %08X 15: %08X'):format(word14b, word15b)) end + if auto then + n = 0 + ed = sd + else + tries = 0 + soon = 0 + late = 0 + end else print(('[=] Status: 15 bitflipped but inactive => '..ansicolors.yellow..'PROMISING: '..ansicolors.reset..'14: %08X 15: '..ansicolors.cyan..'%08X'..ansicolors.reset):format(word14, word15)) end - if auto then - n = 0 - ed = sd - else - tries = 0 - soon = 0 - late = 0 - end end end if not auto then From 275986e74398b7487f285bff427deda55cb65d57 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Thu, 15 Oct 2020 23:54:18 +0200 Subject: [PATCH 42/58] exits --- client/src/cmdlfem4x.c | 75 ++++++++++++++++++++++-------------------- 1 file changed, 40 insertions(+), 35 deletions(-) diff --git a/client/src/cmdlfem4x.c b/client/src/cmdlfem4x.c index 29ab12287..0f2d11431 100644 --- a/client/src/cmdlfem4x.c +++ b/client/src/cmdlfem4x.c @@ -1680,8 +1680,10 @@ static int unlock_write_protect(bool use_pwd, uint32_t pwd, uint32_t data, bool return status; } -static int unlock_reset(bool use_pwd, uint32_t pwd, uint32_t data) { - PrintAndLogEx(INFO, "resetting the " _RED_("active") " lock block"); +static int unlock_reset(bool use_pwd, uint32_t pwd, uint32_t data, bool verbose) { + if (verbose) + PrintAndLogEx(INFO, "resetting the " _RED_("active") " lock block"); + return unlock_write_protect(use_pwd, pwd, data, false); } @@ -1776,7 +1778,7 @@ static int CmdEM4x05Unlock(const char *Cmd) { PrintAndLogEx(INFO, "--------------- " _CYAN_("EM4x05 tear-off : target PROTECT") " -----------------------\n"); - PrintAndLogEx(INFO, "initial prot 14&15 [ " _GREEN_("%08X") ", " _GREEN_("%08X") " ]", init_14, init_15); + PrintAndLogEx(INFO, "initial prot 14&15 [ " _GREEN_("%08X") ", " _GREEN_("%08X") " ]", init_14, init_15); if (use_pwd) { PrintAndLogEx(INFO, " target password [ " _GREEN_("%08"PRIX64) " ]", pwd); @@ -1791,8 +1793,11 @@ static int CmdEM4x05Unlock(const char *Cmd) { PrintAndLogEx(INFO, " write value [ " _GREEN_("%08X") " ]", write_value); PrintAndLogEx(INFO, "----------------------------------------------------------------------------\n"); + PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "press " _YELLOW_("'enter'") " to cancel the command"); - + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "--------------- " _CYAN_("start") " -----------------------\n"); + int exit_code = PM3_SUCCESS; uint32_t word14 = 0, word15 = 0; uint32_t tries = 0; @@ -1807,7 +1812,7 @@ static int CmdEM4x05Unlock(const char *Cmd) { while (start <= end) { if (my_auto && n < 1) { - PrintAndLogEx(INFO, "Reached n < 1 => " _YELLOW_("disabling automatic mode")); + PrintAndLogEx(INFO, "Reached n < 1 => " _YELLOW_("disabling automatic mode")); end = start; my_auto = false; n = 0; @@ -1833,20 +1838,11 @@ static int CmdEM4x05Unlock(const char *Cmd) { late = 0; } - - if (is_cancelled()) { exit_code = PM3_EOPABORTED; break; } - - /* - if ( start != prev_delay) { - PrintAndLogEx(INFO, "Tear-off delay hook configured => " _GREEN_("%.0lf us"), start); - prev_delay = start; - } - */ - + // set tear off trigger clearCommandBuffer(); tearoff_params_t params = { @@ -1857,7 +1853,7 @@ static int CmdEM4x05Unlock(const char *Cmd) { res = handle_tearoff(¶ms, verbose); if ( res != PM3_SUCCESS ) { PrintAndLogEx(WARNING, "failed to configure tear off"); - return PM3_EOPABORTED; + return PM3_ESOFT; } // write @@ -1865,18 +1861,16 @@ static int CmdEM4x05Unlock(const char *Cmd) { // read after trigger res = EM4x05ReadWord_ext(14, pwd, use_pwd, &word14); - if (res == PM3_SUCCESS) { - //PrintAndLogEx(INFO, "14 after [ " _GREEN_("%08X") " ]", word14); - } else { - break; + if (res != PM3_SUCCESS) { + PrintAndLogEx(WARNING, "failed to read 14"); + return PM3_ESOFT; } // read after trigger res = EM4x05ReadWord_ext(15, pwd, use_pwd, &word15); - if (res == PM3_SUCCESS) { - //PrintAndLogEx(INFO, "15 after [ " _GREEN_("%08X") " ]", word15); - } else { - break; + if (res != PM3_SUCCESS) { + PrintAndLogEx(WARNING, "failed to read 15"); + return PM3_ESOFT; } if (verbose) @@ -1900,36 +1894,45 @@ static int CmdEM4x05Unlock(const char *Cmd) { PrintAndLogEx(INFO, "Status: Protect succeeded => " _GREEN_("tearing too late")); } else { if ( word14 == search_value) { - PrintAndLogEx(INFO, "Status: 15 ok, 14 not yet erased => " _GREEN_("tearing too late")); + PrintAndLogEx(INFO, "Status: 15 ok, 14 not yet erased => " _GREEN_("tearing too late")); } else { - PrintAndLogEx(INFO, "Status: 15 ok, 14 partially erased => " _GREEN_("tearing too late")); + PrintAndLogEx(INFO, "Status: 15 ok, 14 partially erased => " _GREEN_("tearing too late")); } } - unlock_reset(use_pwd, pwd, write_value); + + unlock_reset(use_pwd, pwd, write_value, verbose); + uint32_t word14b = 0, word15b = 0; + // read after reset res = EM4x05ReadWord_ext(14, pwd, use_pwd, &word14b); if (res != PM3_SUCCESS) { - break; + PrintAndLogEx(WARNING, "failed to read 14"); + return PM3_ESOFT; } if (word14b == 0) { - unlock_reset(use_pwd, pwd, write_value); + + unlock_reset(use_pwd, pwd, write_value, verbose); + res = EM4x05ReadWord_ext(14, pwd, use_pwd, &word14b); if (res != PM3_SUCCESS) { - break; + PrintAndLogEx(WARNING, "failed to read 14"); + return PM3_ESOFT; } } if (word14b != search_value) { - // read after reset res = EM4x05ReadWord_ext(15, pwd, use_pwd, &word15b); if (res == PM3_SUCCESS) { PrintAndLogEx(INFO, "Status: new definitive value! => " _RED_("SUCCESS:") " 14: " _CYAN_("%08X") " 15: %08X", word14b, word15b); success = true; + break; + } else { + PrintAndLogEx(WARNING, "failed to read 15"); + return PM3_ESOFT; } - break; } if (my_auto) { end = start; @@ -1947,18 +1950,20 @@ static int CmdEM4x05Unlock(const char *Cmd) { PrintAndLogEx(INFO, "Status: 15 bitflipped and active => " _RED_("SUCCESS?: ") "14: %08X 15: " _CYAN_("%08X"), word14, word15); PrintAndLogEx(INFO, "Committing results..."); - unlock_reset(use_pwd, pwd, write_value); + unlock_reset(use_pwd, pwd, write_value, verbose); uint32_t word14b = 0, word15b = 0; + // read after reset res = EM4x05ReadWord_ext(14, pwd, use_pwd, &word14b); if ( res != PM3_SUCCESS ) { PrintAndLogEx(WARNING, "failed to read 14"); - return PM3_EOPABORTED; + return PM3_ESOFT; } + res = EM4x05ReadWord_ext(15, pwd, use_pwd, &word15b); if ( res != PM3_SUCCESS ) { PrintAndLogEx(WARNING, "failed to read 15"); - return PM3_EOPABORTED; + return PM3_ESOFT; } if (verbose) From b589699c38cbac4cc6470a58399d3d29e04da43e Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Fri, 16 Oct 2020 00:11:52 +0200 Subject: [PATCH 43/58] em: show bitflips --- client/src/cmdlfem4x.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/client/src/cmdlfem4x.c b/client/src/cmdlfem4x.c index 0f2d11431..4e01c2466 100644 --- a/client/src/cmdlfem4x.c +++ b/client/src/cmdlfem4x.c @@ -1800,6 +1800,7 @@ static int CmdEM4x05Unlock(const char *Cmd) { int exit_code = PM3_SUCCESS; uint32_t word14 = 0, word15 = 0; + uint32_t word14b = 0, word15b = 0; uint32_t tries = 0; uint32_t soon = 0; uint32_t late = 0; @@ -1902,8 +1903,6 @@ static int CmdEM4x05Unlock(const char *Cmd) { unlock_reset(use_pwd, pwd, write_value, verbose); - uint32_t word14b = 0, word15b = 0; - // read after reset res = EM4x05ReadWord_ext(14, pwd, use_pwd, &word14b); if (res != PM3_SUCCESS) { @@ -1951,7 +1950,6 @@ static int CmdEM4x05Unlock(const char *Cmd) { PrintAndLogEx(INFO, "Committing results..."); unlock_reset(use_pwd, pwd, write_value, verbose); - uint32_t word14b = 0, word15b = 0; // read after reset res = EM4x05ReadWord_ext(14, pwd, use_pwd, &word14b); @@ -2008,8 +2006,17 @@ static int CmdEM4x05Unlock(const char *Cmd) { PrintAndLogEx(INFO, "----------------------------- " _CYAN_("exit") " ----------------------------------\n"); t1 = msclock() - t1; PrintAndLogEx(SUCCESS, "\ntime in unlock " _YELLOW_("%.0f") " seconds\n", (float)t1 / 1000.0); - if (success) - PrintAndLogEx(INFO, "try " _YELLOW_("`lf em 4x05_dump`")); + if (success) { + uint32_t bitflips = search_value ^ word14b; + PrintAndLogEx(INFO, "Old protection word => " _YELLOW_("%08X"), search_value); + char bitstring[9] = {0}; + for (int i=0; i < 8; i++) { + bitstring[i] = bitflips & (0xF << ((7-i) * 4)) ? 'x' : '.'; + } + PrintAndLogEx(INFO, "Bitflips => %s", bitstring); + PrintAndLogEx(INFO, "New protection word => " _CYAN_("%08X") "\n", word14b); + PrintAndLogEx(INFO, "Try " _YELLOW_("`lf em 4x05_dump`")); + } PrintAndLogEx(NORMAL, ""); return exit_code; } From d4e6a5b7a6844b2539556521d6a28f3e2c6d6032 Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Fri, 16 Oct 2020 01:30:08 +0200 Subject: [PATCH 44/58] em: display nr of flips --- client/src/cmdlfem4x.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/client/src/cmdlfem4x.c b/client/src/cmdlfem4x.c index 4e01c2466..db6979dfe 100644 --- a/client/src/cmdlfem4x.c +++ b/client/src/cmdlfem4x.c @@ -2013,7 +2013,12 @@ static int CmdEM4x05Unlock(const char *Cmd) { for (int i=0; i < 8; i++) { bitstring[i] = bitflips & (0xF << ((7-i) * 4)) ? 'x' : '.'; } - PrintAndLogEx(INFO, "Bitflips => %s", bitstring); + // compute number of bits flipped + uint32_t i = bitflips; + i = i - ((i >> 1) & 0x55555555); + i = (i & 0x33333333) + ((i >> 2) & 0x33333333); + i = (((i + (i >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24; + PrintAndLogEx(INFO, "Bitflips: %2u events => %s", i, bitstring); PrintAndLogEx(INFO, "New protection word => " _CYAN_("%08X") "\n", word14b); PrintAndLogEx(INFO, "Try " _YELLOW_("`lf em 4x05_dump`")); } From ffffa77dd9e4b7aee16a705ef99ce41631e730a5 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Fri, 16 Oct 2020 07:49:51 +0200 Subject: [PATCH 45/58] some more stats --- client/src/cmdlfem4x.c | 52 +++++++++++++++++++++++++++++++++++++----- client/src/util.c | 37 ++++++++++++++++++++++++++++++ client/src/util.h | 5 ++++ 3 files changed, 88 insertions(+), 6 deletions(-) diff --git a/client/src/cmdlfem4x.c b/client/src/cmdlfem4x.c index db6979dfe..1bcd5b244 100644 --- a/client/src/cmdlfem4x.c +++ b/client/src/cmdlfem4x.c @@ -1645,6 +1645,10 @@ static int CmdEM4x05Chk(const char *Cmd) { return PM3_SUCCESS; } +typedef struct { + uint16_t cnt; + uint32_t value; +} em4x05_unlock_item_t; static int unlock_write_protect(bool use_pwd, uint32_t pwd, uint32_t data, bool verbose) { @@ -1686,6 +1690,22 @@ static int unlock_reset(bool use_pwd, uint32_t pwd, uint32_t data, bool verbose) return unlock_write_protect(use_pwd, pwd, data, false); } +static void unlock_add_item(em4x05_unlock_item_t *array, uint8_t len, uint32_t value) { + + uint8_t i = 0; + for (; i < len; i++) { + if ( array[i].cnt == 0) + break; + + if ( array[i].value == value) { + array[i].cnt++; + return; + } + } + + array[i].cnt++; + array[i].value = value; +} static int CmdEM4x05Unlock(const char *Cmd) { @@ -1804,10 +1824,12 @@ static int CmdEM4x05Unlock(const char *Cmd) { uint32_t tries = 0; uint32_t soon = 0; uint32_t late = 0; + + em4x05_unlock_item_t flipped[64]; + // // main loop // - //uint32_t prev_delay = 0; bool success = false; uint64_t t1 = msclock(); while (start <= end) { @@ -1971,12 +1993,16 @@ static int CmdEM4x05Unlock(const char *Cmd) { if (word14b == word15) { PrintAndLogEx(INFO, "Status: confirmed => " _RED_("SUCCESS: ") "14: " _CYAN_("%08X") " 15: %08X", word14b, word15b); + + unlock_add_item(flipped, 64, word14b); success = true; break; } if (word14b != search_value) { PrintAndLogEx(INFO, "Status: new definitive value! => " _RED_("SUCCESS: ") "14: " _CYAN_("%08X") " 15: %08X", word14b, word15b); + + unlock_add_item(flipped, 64, word14b); success = true; break; } @@ -1993,6 +2019,9 @@ static int CmdEM4x05Unlock(const char *Cmd) { } } else { PrintAndLogEx(INFO, "Status: 15 bitflipped but inactive => " _YELLOW_("PROMISING: ") "14: %08X 15: " _CYAN_("%08X"), word14, word15); + + unlock_add_item(flipped, 64, word15); + soon ++; } } @@ -2014,14 +2043,25 @@ static int CmdEM4x05Unlock(const char *Cmd) { bitstring[i] = bitflips & (0xF << ((7-i) * 4)) ? 'x' : '.'; } // compute number of bits flipped - uint32_t i = bitflips; - i = i - ((i >> 1) & 0x55555555); - i = (i & 0x33333333) + ((i >> 2) & 0x33333333); - i = (((i + (i >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24; - PrintAndLogEx(INFO, "Bitflips: %2u events => %s", i, bitstring); + + PrintAndLogEx(INFO, "Bitflips: %2u events => %s", bitcount32(bitflips), bitstring); PrintAndLogEx(INFO, "New protection word => " _CYAN_("%08X") "\n", word14b); + + PrintAndLogEx(INFO, "Try " _YELLOW_("`lf em 4x05_dump`")); } + + if (verbose) { + PrintAndLogEx(NORMAL, "Stats:"); + PrintAndLogEx(INFO, " idx | value | cnt | flipped bits"); + PrintAndLogEx(INFO, "-----+----------+-----+------"); + for (uint8_t i = 0; i < 64; i++) { + if (flipped[i].cnt == 0) + break; + + PrintAndLogEx(INFO, " %3u | %08X | %3u | %u", i, flipped[i].value, flipped[i].cnt, bitcount32(search_value ^ flipped[i].value)); + } + } PrintAndLogEx(NORMAL, ""); return exit_code; } diff --git a/client/src/util.c b/client/src/util.c index 161115993..6a49a27dd 100644 --- a/client/src/util.c +++ b/client/src/util.c @@ -895,3 +895,40 @@ int hexstring_to_u96(uint32_t *hi2, uint32_t *hi, uint32_t *lo, const char *str) } return i - 1; } + +inline uint32_t bitcount32(uint32_t a) { +#if defined __GNUC__ + return __builtin_popcountl(a); +#else + a = a - ((a >> 1) & 0x55555555); + a = (a & 0x33333333) + ((a >> 2) & 0x33333333); + return (((a + (a >> 4)) & 0x0f0f0f0f) * 0x01010101) >> 24; +#endif +} + +inline uint64_t bitcount64(uint64_t a) { +#if defined __GNUC__ + return __builtin_popcountll(a); +#else + PrintAndLogEx(FAILED, "Was not compiled with fct bitcount64"); + return 0; +#endif +} + +inline uint32_t leadingzeros32(uint32_t a) { +#if defined __GNUC__ + return __builtin_clzl(a); +#else + PrintAndLogEx(FAILED, "Was not compiled with fct bitcount64"); + return 0; +#endif +} + +inline uint64_t leadingzeros64(uint64_t a) { +#if defined __GNUC__ + return __builtin_clzll(a); +#else + PrintAndLogEx(FAILED, "Was not compiled with fct bitcount64"); + return 0; +#endif +} diff --git a/client/src/util.h b/client/src/util.h index 663abbc55..f65c94622 100644 --- a/client/src/util.h +++ b/client/src/util.h @@ -101,4 +101,9 @@ void strcreplace(char *buf, size_t len, char from, char to); char *str_dup(const char *src); char *str_ndup(const char *src, size_t len); int hexstring_to_u96(uint32_t *hi2, uint32_t *hi, uint32_t *lo, const char *str); + +uint32_t bitcount32(uint32_t a); +uint64_t bitcount64(uint64_t a); +uint32_t leadingzeros32(uint32_t a); +uint64_t leadingzeros64(uint64_t a); #endif From a7b7e0ba9b3a06b69706449e29c264dcd67e47e4 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Fri, 16 Oct 2020 14:02:21 +0200 Subject: [PATCH 46/58] fix overflow --- client/src/cmdlfem4x.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/client/src/cmdlfem4x.c b/client/src/cmdlfem4x.c index 1bcd5b244..5e008b369 100644 --- a/client/src/cmdlfem4x.c +++ b/client/src/cmdlfem4x.c @@ -1703,6 +1703,9 @@ static void unlock_add_item(em4x05_unlock_item_t *array, uint8_t len, uint32_t v } } + if (i >= len ) + return; + array[i].cnt++; array[i].value = value; } From 1dfd2f512798e31513173a10100b8d277cfd3ffd Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Fri, 16 Oct 2020 14:15:12 +0200 Subject: [PATCH 47/58] simpler --- client/src/cmdlfem4x.c | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/client/src/cmdlfem4x.c b/client/src/cmdlfem4x.c index 5e008b369..242c37307 100644 --- a/client/src/cmdlfem4x.c +++ b/client/src/cmdlfem4x.c @@ -1694,20 +1694,16 @@ static void unlock_add_item(em4x05_unlock_item_t *array, uint8_t len, uint32_t v uint8_t i = 0; for (; i < len; i++) { - if ( array[i].cnt == 0) - break; - - if ( array[i].value == value) { + if ( array[i].value == value ) { array[i].cnt++; - return; + break; + } + if ( array[i].cnt == 0 ) { + array[i].cnt++; + array[i].value = value; + break; } } - - if (i >= len ) - return; - - array[i].cnt++; - array[i].value = value; } static int CmdEM4x05Unlock(const char *Cmd) { From 91a1520ce3b27420771c5d2dd7acdc5282418a81 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Fri, 16 Oct 2020 14:19:52 +0200 Subject: [PATCH 48/58] disabling stuff --- client/src/cmdhfmfu.c | 368 +++++++++++++++++++++++++++++++++++------- 1 file changed, 311 insertions(+), 57 deletions(-) diff --git a/client/src/cmdhfmfu.c b/client/src/cmdhfmfu.c index 771ae0e41..bda3d5732 100644 --- a/client/src/cmdhfmfu.c +++ b/client/src/cmdhfmfu.c @@ -21,6 +21,7 @@ #include "generator.h" #include "mifare/ndef.h" #include "cliparser.h" +#include "cmdmain.h" #define MAX_UL_BLOCKS 0x0F @@ -343,21 +344,28 @@ static int ul_send_cmd_raw(uint8_t *cmd, uint8_t cmdlen, uint8_t *response, uint return resplen; } -static int ul_select(iso14a_card_select_t *card) { +static bool ul_select(iso14a_card_select_t *card) { ul_switch_on_field(); PacketResponseNG resp; - bool ans = WaitForResponseTimeout(CMD_ACK, &resp, 1500); - - if (!ans || resp.oldarg[0] < 1) { - PrintAndLogEx(WARNING, "iso14443a card select failed"); + if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) { + PrintAndLogEx(WARNING, "timeout while waiting for reply."); DropField(); - return 0; - } + return false; + } else { - memcpy(card, resp.data.asBytes, sizeof(iso14a_card_select_t)); - return 1; + uint16_t len = (resp.oldarg[1] & 0xFFFF); + if (len == 0) { + PrintAndLogEx(WARNING, "iso14443a card select failed"); + DropField(); + return false; + } + + if (card) + memcpy(card, resp.data.asBytes, sizeof(iso14a_card_select_t)); + } + return true; } // This read command will at least return 16bytes. @@ -885,7 +893,7 @@ static int ulev1_print_counters(void) { len = ulev1_readCounter(i, counter, sizeof(counter)); if (len == 3) { PrintAndLogEx(INFO, " [%0d]: %s", i, sprint_hex(counter, 3)); - PrintAndLogEx(SUCCESS, " - %02X tearing (" _GREEN_("%s") ")", tear[0], (tear[0] == 0xBD) ? "ok" : "failure"); + PrintAndLogEx(SUCCESS, " - %02X tearing (%s)", tear[0], (tear[0] == 0xBD) ? _GREEN_("ok") : _RED_("failure")); } } return len; @@ -1027,11 +1035,10 @@ static int ulc_magic_test(){ iso14a_card_select_t card; uint8_t nonce1[11] = {0x00}; uint8_t nonce2[11] = {0x00}; - int status = ul_select(&card); - if ( !status ){ + if ( !ul_select(&card) ){ return UL_ERROR; } - status = ulc_requestAuthentication(nonce1, sizeof(nonce1)); + int status = ulc_requestAuthentication(nonce1, sizeof(nonce1)); if ( status > 0 ) { status = ulc_requestAuthentication(nonce2, sizeof(nonce2)); returnValue = ( !memcmp(nonce1, nonce2, 11) ) ? UL_C_MAGIC : UL_C; @@ -1620,7 +1627,7 @@ static int CmdHF14AMfUWrBl(const char *Cmd) { PrintAndLogEx(WARNING, "Command execute timeout"); } - return 0; + return PM3_SUCCESS; } // // Read Single Block @@ -2839,12 +2846,10 @@ static int CmdHF14AMfuOtpTearoff(const char *Cmd) { return usage_hf_mfu_otp_tearoff(); case 'b': blockNoUint = param_get8(Cmd, cmdp + 1); -/* if (blockNoUint < 2) { PrintAndLogEx(WARNING, "Wrong block number"); errors = true; } -*/ cmdp += 2; break; case 'i': @@ -2942,11 +2947,19 @@ static int CmdHF14AMfuOtpTearoff(const char *Cmd) { clearCommandBuffer(); SendCommandMIX(CMD_HF_MFU_OTP_TEAROFF, blockNoUint, actualTime, 0, teardata, 8); + + // we be getting ACK that we are silently ignoring here.. + if (!WaitForResponseTimeout(CMD_HF_MFU_OTP_TEAROFF, &resp, 2000)) { PrintAndLogEx(WARNING, "Failed"); return PM3_ESOFT; } + if (resp.status != PM3_SUCCESS) { + PrintAndLogEx(WARNING, "Tear off reporting failure to select tag"); + continue; + } + got_post = false; clearCommandBuffer(); SendCommandMIX(CMD_HF_MIFAREU_READBL, blockNoUint, 0, 0, NULL, 0); @@ -3046,6 +3059,34 @@ static int CmdHF14AMfuOtpTearoff(const char *Cmd) { return PM3_SUCCESS; } +static int counter_reset_tear(iso14a_card_select_t *card, uint8_t cnt_no) { + + PrintAndLogEx(INFO, "Reset tear check"); + + uint8_t cw[6] = { MIFARE_ULEV1_INCR_CNT, cnt_no, 0x00, 0x00, 0x00, 0x00}; + uint8_t ct[1] = {0}; + uint8_t resp[10] = {0}; + + if (ul_select(card) == false) { + PrintAndLogEx(FAILED, "failed to select card, exiting..."); + return PM3_ESOFT; + } + if (ul_send_cmd_raw(cw, sizeof(cw), resp, sizeof(resp) < 0)) { + PrintAndLogEx(FAILED, "failed to write all ZEROS"); + return PM3_ESOFT; + } + if (ulev1_readTearing(cnt_no, ct, sizeof(ct)) < 0) { + PrintAndLogEx(FAILED, "AFTER, failed to read ANTITEAR, exiting..."); + return PM3_ESOFT; + } + DropField(); + + if (ct[0] != 0xBD) { + PrintAndLogEx(INFO, "Resetting seem to have failed, WHY!?"); + return PM3_ESOFT; + } + return PM3_SUCCESS; +} static int CmdHF14AMfuEv1CounterTearoff(const char *Cmd) { @@ -3062,14 +3103,31 @@ static int CmdHF14AMfuEv1CounterTearoff(const char *Cmd) { arg_int0("c", "cnt", "<0,1,2>", "Target this EV1 counter (0,1,2)"), arg_int0("i", "inc", "", "time interval to increase in each iteration - default 10 us"), arg_int0("l", "limit", "", "test upper limit time - default 3000 us"), - arg_int0("s", "start", "", "test start time - default 0 us"), + arg_int0("s", "start", "", "test start time - default 0 us"), + arg_int0(NULL, "fix", "", "test fixed loop delay"), + arg_str0("x", "hex", NULL, "3 byte hex to increase counter with"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); + + int interval = 0; + int time_limit, start_time = 0; int counter = arg_get_int_def(ctx, 1, 0); - int interval = arg_get_int_def(ctx, 2, 10); - int time_limit = arg_get_int_def(ctx, 3, 3000); - int start_time = arg_get_int_def(ctx, 4, 0); + int fixed = arg_get_int_def(ctx, 5, -1); + + if ( fixed == -1 ) { + interval = arg_get_int_def(ctx, 2, 10); + time_limit = arg_get_int_def(ctx, 3, 3000); + start_time = arg_get_int_def(ctx, 4, 0); + } else { + start_time = fixed; + interval = 0; + time_limit = fixed; + } + + uint8_t newvalue[5] = {0}; + int newvaluelen = 0; + CLIGetHexWithReturn(ctx, 6, newvalue, &newvaluelen); CLIParserFree(ctx); // Validations @@ -3095,106 +3153,302 @@ static int CmdHF14AMfuEv1CounterTearoff(const char *Cmd) { iso14a_card_select_t card; - if (ul_select(&card) == 0) { - PrintAndLogEx(WARNING, "Failed to select card"); + // reset counter tear + counter_reset_tear(&card, cnt_no); + + if (ul_select(&card) == false) { + PrintAndLogEx(INFO, "failed to select card, exiting..."); return PM3_ESOFT; } - + uint8_t inital_cnt[3] = {0, 0, 0}; int len = ulev1_readCounter(cnt_no, inital_cnt, sizeof(inital_cnt)); - DropField(); if ( len != sizeof(inital_cnt) ) { - PrintAndLogEx(WARNING, "Failed to read counter"); + PrintAndLogEx(WARNING, "failed to read counter"); + return PM3_ESOFT; + } + + uint8_t inital_tear[1] = {0}; + len = ulev1_readTearing(cnt_no, inital_tear, sizeof(inital_tear)); + DropField(); + if ( len != sizeof(inital_tear) ) { + PrintAndLogEx(WARNING, "failed to read ANTITEAR, exiting... %d", len); return PM3_ESOFT; } + uint32_t wr_value = ( newvalue[0] | newvalue[1] << 8 | newvalue[2] << 16 ); + uint32_t inital_value = ( inital_cnt[0] | inital_cnt[1] << 8 | inital_cnt[2] << 16 );; + PrintAndLogEx(INFO, "----------------- " _CYAN_("MFU Ev1 Counter Tear off") " ---------------------"); - PrintAndLogEx(INFO, "Target counter no " _GREEN_("%u"), counter); - PrintAndLogEx(INFO, "Target initial value " _GREEN_("%s"), sprint_hex_inrow(inital_cnt, sizeof(inital_cnt))); + PrintAndLogEx(INFO, "Target counter no [ " _GREEN_("%u") " ]", counter); + PrintAndLogEx(INFO, " counter value [ " _GREEN_("%s") " ]", sprint_hex_inrow(inital_cnt, sizeof(inital_cnt))); + PrintAndLogEx(INFO, " anti-tear value [ " _GREEN_("%02X") " ]", inital_tear[0]); + PrintAndLogEx(INFO, " increase value [ " _GREEN_("%s") " ]", sprint_hex_inrow(newvalue, newvaluelen)); PrintAndLogEx(INFO, "----------------------------------------------------"); - bool got_pre = false, got_post = false; + uint8_t pre_tear = 0, post_tear = 0; uint8_t pre[3] = {0}; uint8_t post[3] = {0}; uint32_t actual_time = start_time; + uint32_t a = 0, b = 0; + uint32_t loop = 0; + uint16_t late = 0; + while (actual_time <= (time_limit - interval)) { + + DropField(); + + loop++; + if (kbd_enter_pressed()) { PrintAndLogEx(INFO, "\naborted via keyboard!\n"); break; } - PrintAndLogEx(INPLACE, "Using tear-off delay " _GREEN_("%" PRIu32) " us", actual_time); + PrintAndLogEx(INPLACE, "Using tear-off delay " _GREEN_("%" PRIu32) " us (attempt %u)", actual_time, loop); - if (ul_select(&card) == 0) - return PM3_ESOFT; + if (ul_select(&card) == false) { + PrintAndLogEx(FAILED, "BEFORE, failed to select card, looping..."); + continue; + } - got_pre = false; - uint8_t cntresp[3] = {0, 0, 0}; + uint8_t cntresp[3] = {0, 0, 0}; int rlen = ulev1_readCounter(cnt_no, cntresp, sizeof(cntresp)); if ( rlen == sizeof(cntresp) ) { memcpy(pre, cntresp, sizeof(pre)); - got_pre = true; + } else { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(FAILED, "BEFORE, failed to read COUNTER, exiting..."); + break; + } + + uint8_t tear[1] = {0}; + int tlen = ulev1_readTearing(cnt_no, tear, sizeof(tear)); + if ( tlen == sizeof(tear) ) { + pre_tear = tear[0]; + } else { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(FAILED, "BEFORE, failed to read ANTITEAR, exiting... %d", tlen); + break; } - DropField(); + DropField(); + struct p { uint8_t counter; uint32_t tearoff_time; + uint8_t value[3]; } PACKED payload; payload.counter = cnt_no; payload.tearoff_time = actual_time; + memcpy(payload.value, newvalue, sizeof(payload.value)); clearCommandBuffer(); PacketResponseNG resp; SendCommandNG(CMD_HF_MFU_COUNTER_TEAROFF, (uint8_t*)&payload, sizeof(payload)); if (!WaitForResponseTimeout(CMD_HF_MFU_COUNTER_TEAROFF, &resp, 2000)) { PrintAndLogEx(WARNING, "\ntear off command failed"); - return PM3_ESOFT; + continue; } - got_post = false; - if (ul_select(&card) == 0) - return PM3_ESOFT; + if (ul_select(&card) == false) { + PrintAndLogEx(FAILED, "AFTER, failed to select card, exiting..."); + break; + } rlen = ulev1_readCounter(cnt_no, cntresp, sizeof(cntresp)); if ( rlen == sizeof(cntresp) ) { memcpy(post, cntresp, sizeof(post)); - got_post = true; + } else { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(FAILED, "AFTER, failed to read COUNTER, exiting..."); + break; } + + tear[0] = 0; + tlen = ulev1_readTearing(cnt_no, tear, sizeof(tear)); + if ( tlen == sizeof(tear) ) { + post_tear = tear[0]; + } else { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(FAILED, "AFTER, failed to read ANTITEAR, exiting..."); + break; + } + DropField(); - if (got_pre && got_post) { + char prestr[20] = {0}; + snprintf(prestr, sizeof(prestr), "%s", sprint_hex_inrow(pre, sizeof(pre))); + char poststr[20] = {0}; + snprintf(poststr, sizeof(poststr), "%s", sprint_hex_inrow(post, sizeof(post))); - char prestr[20] = {0}; - snprintf(prestr, sizeof(prestr), "%s", sprint_hex_inrow(pre, sizeof(pre))); - char poststr[20] = {0}; - snprintf(poststr, sizeof(poststr), "%s", sprint_hex_inrow(post, sizeof(post))); + bool post_tear_check = (post_tear == 0xBD); + a = (pre[0] | pre[1] << 8 | pre[2] << 16); + b = (post[0] | post[1] << 8 | post[2] << 16); + + // A != B + if (memcmp(pre, post, sizeof(pre)) != 0) { - if (memcmp(pre, post, sizeof(pre)) == 0) { -// PrintAndLogEx(INFO, "Current %d - %s", cnt_no, poststr); + + PrintAndLogEx(NORMAL, ""); + + if (inital_value != a ) { + + if ( inital_value != b ) + PrintAndLogEx(INFO, "pre %08x, post %08x != inital %08x | tear: 0x%02X == 0x%02X", a, b, inital_value, pre_tear, post_tear); + else + PrintAndLogEx(INFO, "pre %08x != inital and post %08x == inital %08x | tear: 0x%02X == 0x%02X", a, b, inital_value, pre_tear, post_tear); } else { - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, _CYAN_("Tear off occured") " : %d %s vs " _RED_("%s") - , cnt_no, prestr, poststr); + + if ( inital_value != b ) + PrintAndLogEx(INFO, "pre %08x == inital and post %08x != inital %08x | tear: 0x%02X == 0x%02X", a, b, inital_value, pre_tear, post_tear); } - } else { - if (got_pre == false) { - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(FAILED, "Failed to read Counter BEFORE"); + if ( b == 0 ) { + PrintAndLogEx(INFO, _CYAN_("Tear off occured (ZEROS value!) -> ") "%s vs " _GREEN_("%s") " Tear status: 0x%02X == 0x%02X ( %s )" + , prestr + , poststr + , pre_tear + , post_tear + , post_tear_check ? _GREEN_("OK") : _RED_("DETECTED") + ); + break; } - if (got_post == false) { - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(FAILED, "Failed to read Counter AFTER"); + + if ( a > b ) { + PrintAndLogEx(INFO, _CYAN_("Tear off occured " _RED_("( LESS )") " -> ") "%s vs " _GREEN_("%s") " Tear status: 0x%02X == 0x%02X ( %s )" + , prestr + , poststr + , pre_tear + , post_tear + , post_tear_check ? _GREEN_("OK") : _RED_("DETECTED") + ); + + + if (counter_reset_tear(&card, cnt_no) != PM3_SUCCESS){ + PrintAndLogEx(FAILED, "failed to reset tear, exiting..."); + break; + } + + uint32_t bar = (0x1000000 - b) + 2; + //wr_value = bar; + //newvalue[0] = (bar) & 0xFF; + //newvalue[1] = ((bar >> 8) & 0xFF); + //newvalue[2] = ((bar >> 16) & 0xFF); + + wr_value = 0; + newvalue[0] = 0; + newvalue[1] = 0; + newvalue[2] = 0; + + PrintAndLogEx(INFO, " 0x1000000 - 0x%x == 0x%x", b, bar); + PrintAndLogEx(INFO, " new increase value 0x%x" , wr_value); + PrintAndLogEx(INFO, " because BAR + post == 0x%x" , bar + b); + + PrintAndLogEx(INFO, "New increase value " _YELLOW_("%s"), sprint_hex_inrow(newvalue, newvaluelen)); + continue; + } else { + + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, _CYAN_("Tear off occured (+1) (too late) -> ") "%s vs %s Tear: 0x%02X == 0x%02X ( %s )" + , prestr + , poststr + , pre_tear + , post_tear + , post_tear_check ? _GREEN_("OK") : _RED_("DETECTED") + ); + + if ( post_tear_check && b == inital_value) { + PrintAndLogEx(INFO, "Reverted to previous value"); + break; + } + if ( wr_value != 0 ) { + + //uint32_t bar = (0x1000000 - b) + 2; + wr_value = 0; + newvalue[0] = 0; + newvalue[1] = 0; + newvalue[2] = 0; + + if ( b >= (inital_value + (2 * wr_value))) { + PrintAndLogEx(INFO, "Large " _YELLOW_("( JUMP )") " detected"); + + /* + wr_value = bar; + newvalue[0] = (bar) & 0xFF; + newvalue[1] = ((bar >> 8) & 0xFF); + newvalue[2] = ((bar >> 16) & 0xFF); + + */ + } else { + /* + wr_value = bar; + newvalue[0] = (bar) & 0xFF; + newvalue[1] = ((bar >> 8) & 0xFF); + newvalue[2] = ((bar >> 16) & 0xFF); + wr_value = 0; + newvalue[0] = 0; + newvalue[1] = 0; + newvalue[2] = 0; + */ + } + + } + PrintAndLogEx(INFO, "New increase value " _YELLOW_("%s"), sprint_hex_inrow(newvalue, newvaluelen)); + + //actual_time--; + late++; + } + } else { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, _CYAN_("Status: same value! -> ") "%s == %s Tear: 0x%02X == 0x%02X ( %s )" + , prestr + , poststr + , pre_tear + , post_tear + , post_tear_check ? _GREEN_("OK") : _RED_("DETECTED") + ); + + if ( post_tear_check ) { + if ( a == b ) { + //actual_time--; + continue; + } + + if ( b == inital_value ) { + PrintAndLogEx(INFO, "Reverted to previous value"); + break; + } + } else { + + if (counter_reset_tear(&card, cnt_no) != PM3_SUCCESS){ + PrintAndLogEx(FAILED, "failed to reset tear, exiting..."); + break; + } + } } actual_time += interval; } + DropField(); + + PrintAndLogEx(INFO, " Sent %u tear offs ", loop); + + counter_reset_tear(&card, cnt_no); + + PrintAndLogEx(INFO, "hf 14a raw -s -c 3900 --> read counter 0"); + PrintAndLogEx(INFO, "hf 14a raw -s -c 3e00 --> read tearing 0"); PrintAndLogEx(NORMAL, ""); + char read_cnt_str[30]; + sprintf(read_cnt_str, "hf 14a raw -s -c 39%02x", counter); + CommandReceived(read_cnt_str); + char read_tear_str[30]; + sprintf(read_tear_str, "hf 14a raw -s -c 3e%02x", counter); + CommandReceived(read_tear_str); return PM3_SUCCESS; } From 867750b7eeff0aff6132e7bf79adbc0ab6610600 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Fri, 16 Oct 2020 16:07:39 +0200 Subject: [PATCH 49/58] fix coverity --- client/src/cmdhficlass.c | 44 +++++----------------------------------- 1 file changed, 5 insertions(+), 39 deletions(-) diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index 57a231e2e..9e61e5d89 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -1804,44 +1804,7 @@ write_dump: } static int iclass_write_block(uint8_t blockno, uint8_t *bldata, uint8_t *KEY, bool use_credit_key, bool elite, bool rawkey, bool replay, bool verbose) { - /* - uint8_t MAC[4] = {0x00, 0x00, 0x00, 0x00}; - uint8_t div_key[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - if (select_and_auth(KEY, MAC, div_key, use_credit_key, elite, rawkey, verbose) == false) { - return PM3_ESOFT; - } - - calc_wb_mac(blockno, bldata, div_key, MAC); - - struct p { - uint8_t blockno; - uint8_t data[12]; - } PACKED payload; - payload.blockno = blockno; - - memcpy(payload.data, bldata, 8); - memcpy(payload.data + 8, MAC, 4); - - - // - typedef struct { - uint8_t key[8]; - bool use_raw; - bool use_elite; - bool use_credit_key; - bool send_reply; - bool do_auth; - uint8_t blockno; - } PACKED iclass_auth_req_t; - - // iCLASS write block request data structure - typedef struct { - iclass_auth_req_t req; - uint8_t data[8]; - } PACKED iclass_writeblock_req_t; - - - */ + iclass_writeblock_req_t payload = { .req.use_raw = rawkey, .req.use_elite = elite, @@ -2129,7 +2092,7 @@ static int CmdHFiClassRestore(const char *Cmd) { free(dump); if (verbose) { - PrintAndLogEx(INFO, "Preparing to restore block range 0x02x..0x%02x", startblock, endblock); + PrintAndLogEx(INFO, "Preparing to restore block range 0x%02x..0x%02x", startblock, endblock); PrintAndLogEx(INFO, "------+----------------------"); PrintAndLogEx(INFO, "block | data"); @@ -2150,6 +2113,7 @@ static int CmdHFiClassRestore(const char *Cmd) { if (WaitForResponseTimeout(CMD_HF_ICLASS_RESTORE, &resp, 2500) == 0) { PrintAndLogEx(WARNING, "command execute timeout"); DropField(); + free(payload); return PM3_ETIMEOUT; } @@ -2159,6 +2123,8 @@ static int CmdHFiClassRestore(const char *Cmd) { } else { PrintAndLogEx(WARNING, "iCLASS restore " _RED_("failed")); } + + free(payload); return resp.status; } From c85962f815d4ddeed816eb8f93a79c75e0089788 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Fri, 16 Oct 2020 17:09:27 +0200 Subject: [PATCH 50/58] fix coverity --- client/src/cmdlfdestron.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/cmdlfdestron.c b/client/src/cmdlfdestron.c index 635f2b4f9..52ae5fc6d 100644 --- a/client/src/cmdlfdestron.c +++ b/client/src/cmdlfdestron.c @@ -71,7 +71,7 @@ int demodDestron(bool verbose) { parity_err += oddparity8(data[i]); data[i] &= 0x7F; } - if (errCnt > 0) { + if (parity_err > 0) { PrintAndLogEx(DEBUG, "DEBUG: Error - Destron: parity errors: %d", parity_err); return PM3_ESOFT; } From 495009804292fd63604c1decd25d35a30cbb52fb Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Fri, 16 Oct 2020 17:10:24 +0200 Subject: [PATCH 51/58] fix coverity --- client/src/scripting.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/src/scripting.c b/client/src/scripting.c index 90e19151c..17362e5f6 100644 --- a/client/src/scripting.c +++ b/client/src/scripting.c @@ -1139,8 +1139,8 @@ static int l_em4x50_read(lua_State *L) { uint32_t addr = 0; sscanf(p_addr, "%u", &addr); - if (addr > 33) - return returnToLuaWithError(L, "Address out-of-range (0..33) got %zu", addr); + if (addr > 31) + return returnToLuaWithError(L, "Address out-of-range (0..31) got %u", addr); // setting up structures em4x50_data_t etd; From 1c8194a84fcba837661b27c8af6bdd3b58178ebc Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Fri, 16 Oct 2020 17:13:05 +0200 Subject: [PATCH 52/58] fix coverity --- client/src/scripting.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/scripting.c b/client/src/scripting.c index 17362e5f6..e603ce359 100644 --- a/client/src/scripting.c +++ b/client/src/scripting.c @@ -1171,7 +1171,7 @@ static int l_em4x50_read(lua_State *L) { PrintAndLogEx(DEBUG, "Addr %u", etd.address); if (etd.pwd_given) - PrintAndLogEx(DEBUG, " Pwd %08X", etd.password); + PrintAndLogEx(DEBUG, " Pwd %s", sprint_hex(etd.password, sizeof(etd.password))); em4x50_word_t words[EM4X50_NO_WORDS]; From 63603cc422e729987dd01c2994c5ab8b0a39ea83 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Fri, 16 Oct 2020 17:13:14 +0200 Subject: [PATCH 53/58] fix coverity --- armsrc/iclass.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/armsrc/iclass.c b/armsrc/iclass.c index 36ba0706b..204cd9ea1 100644 --- a/armsrc/iclass.c +++ b/armsrc/iclass.c @@ -1879,11 +1879,6 @@ void iClass_WriteBlock(uint8_t *msg) { return; } else { - if (resp == NULL) { - res = true; - break; - } - if (GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_UPDATE, &eof_time) == 10) { res = true; break; From e41e87101419f98ada7f2ded0d9e6d80e80121f7 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Fri, 16 Oct 2020 17:15:04 +0200 Subject: [PATCH 54/58] fix coverity --- client/src/cmdlfem4x.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/src/cmdlfem4x.c b/client/src/cmdlfem4x.c index 242c37307..fadb4a7f2 100644 --- a/client/src/cmdlfem4x.c +++ b/client/src/cmdlfem4x.c @@ -716,7 +716,7 @@ static bool EM_ColParityTest(uint8_t *bs, size_t size, uint8_t rows, uint8_t col static bool downloadSamplesEM(void) { // 8 bit preamble + 32 bit word response (max clock (128) * 40bits = 5120 samples) - uint8_t got[5500]; + uint8_t got[6000]; if (!GetFromDevice(BIG_BUF, got, sizeof(got), 0, NULL, 0, NULL, 2500, false)) { PrintAndLogEx(WARNING, "(downloadSamplesEM) command execution time out"); return false; @@ -1800,7 +1800,7 @@ static int CmdEM4x05Unlock(const char *Cmd) { PrintAndLogEx(INFO, "initial prot 14&15 [ " _GREEN_("%08X") ", " _GREEN_("%08X") " ]", init_14, init_15); if (use_pwd) { - PrintAndLogEx(INFO, " target password [ " _GREEN_("%08"PRIX64) " ]", pwd); + PrintAndLogEx(INFO, " target password [ " _GREEN_("%08X") " ]", pwd); } if (my_auto) { PrintAndLogEx(INFO, " automatic mode [ " _GREEN_("enabled") " ]"); From c6902245f058581c84bd06eacfd12433526c0d74 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Fri, 16 Oct 2020 17:22:12 +0200 Subject: [PATCH 55/58] fix coverity --- client/src/cmdhfmfu.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/client/src/cmdhfmfu.c b/client/src/cmdhfmfu.c index bda3d5732..6437d11eb 100644 --- a/client/src/cmdhfmfu.c +++ b/client/src/cmdhfmfu.c @@ -3071,7 +3071,7 @@ static int counter_reset_tear(iso14a_card_select_t *card, uint8_t cnt_no) { PrintAndLogEx(FAILED, "failed to select card, exiting..."); return PM3_ESOFT; } - if (ul_send_cmd_raw(cw, sizeof(cw), resp, sizeof(resp) < 0)) { + if (ul_send_cmd_raw(cw, sizeof(cw), resp, sizeof(resp)) < 0) { PrintAndLogEx(FAILED, "failed to write all ZEROS"); return PM3_ESOFT; } @@ -3452,7 +3452,6 @@ static int CmdHF14AMfuEv1CounterTearoff(const char *Cmd) { return PM3_SUCCESS; } - static int CmdHF14MfuNDEF(const char *Cmd) { int keylen; From d27a8abe809bcf8f03c862b012632571d49f81d1 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Fri, 16 Oct 2020 17:42:28 +0200 Subject: [PATCH 56/58] WIP, not suitable for running --- client/src/cmdhfmfu.c | 46 ++++++++++++++++++++++--------------------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/client/src/cmdhfmfu.c b/client/src/cmdhfmfu.c index 6437d11eb..c472b8d59 100644 --- a/client/src/cmdhfmfu.c +++ b/client/src/cmdhfmfu.c @@ -3059,6 +3059,7 @@ static int CmdHF14AMfuOtpTearoff(const char *Cmd) { return PM3_SUCCESS; } +/* static int counter_reset_tear(iso14a_card_select_t *card, uint8_t cnt_no) { PrintAndLogEx(INFO, "Reset tear check"); @@ -3087,7 +3088,9 @@ static int counter_reset_tear(iso14a_card_select_t *card, uint8_t cnt_no) { } return PM3_SUCCESS; } +*/ +/* static int CmdHF14AMfuEv1CounterTearoff(const char *Cmd) { CLIParserContext *ctx; @@ -3333,10 +3336,10 @@ static int CmdHF14AMfuEv1CounterTearoff(const char *Cmd) { } uint32_t bar = (0x1000000 - b) + 2; - //wr_value = bar; - //newvalue[0] = (bar) & 0xFF; - //newvalue[1] = ((bar >> 8) & 0xFF); - //newvalue[2] = ((bar >> 16) & 0xFF); + // wr_value = bar; + // newvalue[0] = (bar) & 0xFF; + // newvalue[1] = ((bar >> 8) & 0xFF); + // newvalue[2] = ((bar >> 16) & 0xFF); wr_value = 0; newvalue[0] = 0; @@ -3375,24 +3378,21 @@ static int CmdHF14AMfuEv1CounterTearoff(const char *Cmd) { if ( b >= (inital_value + (2 * wr_value))) { PrintAndLogEx(INFO, "Large " _YELLOW_("( JUMP )") " detected"); - /* - wr_value = bar; - newvalue[0] = (bar) & 0xFF; - newvalue[1] = ((bar >> 8) & 0xFF); - newvalue[2] = ((bar >> 16) & 0xFF); - - */ + + // wr_value = bar; + // newvalue[0] = (bar) & 0xFF; + // newvalue[1] = ((bar >> 8) & 0xFF); + // newvalue[2] = ((bar >> 16) & 0xFF); } else { - /* - wr_value = bar; - newvalue[0] = (bar) & 0xFF; - newvalue[1] = ((bar >> 8) & 0xFF); - newvalue[2] = ((bar >> 16) & 0xFF); - wr_value = 0; - newvalue[0] = 0; - newvalue[1] = 0; - newvalue[2] = 0; - */ + + // wr_value = bar; + // newvalue[0] = (bar) & 0xFF; + // newvalue[1] = ((bar >> 8) & 0xFF); + // newvalue[2] = ((bar >> 16) & 0xFF); + // wr_value = 0; + // newvalue[0] = 0; + // newvalue[1] = 0; + // newvalue[2] = 0; } } @@ -3452,6 +3452,8 @@ static int CmdHF14AMfuEv1CounterTearoff(const char *Cmd) { return PM3_SUCCESS; } +*/ + static int CmdHF14MfuNDEF(const char *Cmd) { int keylen; @@ -3582,7 +3584,7 @@ static command_t CommandTable[] = { {"gen", CmdHF14AMfUGenDiverseKeys, AlwaysAvailable, "Generate 3des mifare diversified keys"}, {"pwdgen", CmdHF14AMfUPwdGen, AlwaysAvailable, "Generate pwd from known algos"}, {"otptear", CmdHF14AMfuOtpTearoff, IfPm3Iso14443a, "Tear-off test on OTP bits"}, - {"countertear", CmdHF14AMfuEv1CounterTearoff, IfPm3Iso14443a, "Tear-off test on Ev1 Counter bits"}, +// {"countertear", CmdHF14AMfuEv1CounterTearoff, IfPm3Iso14443a, "Tear-off test on Ev1 Counter bits"}, {"ndef", CmdHF14MfuNDEF, IfPm3Iso14443a, "Prints NDEF records from card"}, {NULL, NULL, NULL, NULL} }; From 4c406ba04ac06705ee7b69074f07d8173d101ea0 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Fri, 16 Oct 2020 17:56:51 +0200 Subject: [PATCH 57/58] thanks @mwalker --- client/dictionaries/t55xx_default_pwds.dic | 2 ++ 1 file changed, 2 insertions(+) diff --git a/client/dictionaries/t55xx_default_pwds.dic b/client/dictionaries/t55xx_default_pwds.dic index cd03c7b7a..b5b981177 100644 --- a/client/dictionaries/t55xx_default_pwds.dic +++ b/client/dictionaries/t55xx_default_pwds.dic @@ -26,6 +26,8 @@ A5B4C3D2 50520901 # default PROX 50524F58 +# blue gun EM4305 +F9DCEBA0 # Default pwd, simple: 00000000 11111111 From 8aa70b37e348027c637521f8b6da7a5f47ac9865 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Fri, 16 Oct 2020 19:13:40 +0200 Subject: [PATCH 58/58] extract em 4x05 into own file --- client/CMakeLists.txt | 1 + client/Makefile | 1 + client/src/cmdlf.c | 3 +- client/src/cmdlfem4x.c | 1441 +------------------------------------ client/src/cmdlfem4x.h | 4 +- client/src/cmdlfem4x05.c | 1474 ++++++++++++++++++++++++++++++++++++++ client/src/cmdlfem4x05.h | 31 + client/src/scripting.c | 2 +- 8 files changed, 1512 insertions(+), 1445 deletions(-) create mode 100644 client/src/cmdlfem4x05.c create mode 100644 client/src/cmdlfem4x05.h diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index e7262ba33..46e77a14f 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -248,6 +248,7 @@ set (TARGET_SOURCES ${PM3_ROOT}/client/src/cmdlfcotag.c ${PM3_ROOT}/client/src/cmdlfdestron.c ${PM3_ROOT}/client/src/cmdlfem4x.c + ${PM3_ROOT}/client/src/cmdlfem4x05.c ${PM3_ROOT}/client/src/cmdlfem4x50.c ${PM3_ROOT}/client/src/cmdlffdxb.c ${PM3_ROOT}/client/src/cmdlfgallagher.c diff --git a/client/Makefile b/client/Makefile index 27702a9d6..b4393ad36 100644 --- a/client/Makefile +++ b/client/Makefile @@ -443,6 +443,7 @@ SRCS = aidsearch.c \ cmdlfcotag.c \ cmdlfdestron.c \ cmdlfem4x.c \ + cmdlfem4x05.c \ cmdlfem4x50.c \ cmdlffdxb.c \ cmdlfguard.c \ diff --git a/client/src/cmdlf.c b/client/src/cmdlf.c index d278d4c9c..f5effbd17 100644 --- a/client/src/cmdlf.c +++ b/client/src/cmdlf.c @@ -30,7 +30,8 @@ #include "cmddata.h" // for `lf search` #include "cmdlfawid.h" // for awid menu #include "cmdlfem4x.h" // for em4x menu -#include "cmdlfem4x50.h" // for em4x50 +#include "cmdlfem4x05.h" // for em4x05 / 4x69 +#include "cmdlfem4x50.h" // for em4x50 #include "cmdlfhid.h" // for hid menu #include "cmdlfhitag.h" // for hitag menu #include "cmdlfidteck.h" // for idteck menu diff --git a/client/src/cmdlfem4x.c b/client/src/cmdlfem4x.c index fadb4a7f2..f20610a2e 100644 --- a/client/src/cmdlfem4x.c +++ b/client/src/cmdlfem4x.c @@ -9,6 +9,7 @@ //----------------------------------------------------------------------------- #include "cmdlfem4x.h" +#include "cmdlfem4x05.h" #include "cmdlfem4x50.h" #include @@ -119,75 +120,6 @@ static int usage_lf_em410x_brute(void) { return PM3_SUCCESS; } -//////////////// 4205 / 4305 commands -static int usage_lf_em4x05_dump(void) { - PrintAndLogEx(NORMAL, "Dump EM4x05/EM4x69. Tag must be on antenna. "); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Usage: lf em 4x05_dump [h] [f ] "); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h - this help"); - PrintAndLogEx(NORMAL, " f - overide filename prefix (optional). Default is based on UID"); - PrintAndLogEx(NORMAL, " pwd - password (hex) (optional)"); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " lf em 4x05_dump"); - PrintAndLogEx(NORMAL, " lf em 4x05_dump 11223344"); - PrintAndLogEx(NORMAL, " lf em 4x05_dump f card1 11223344"); - return PM3_SUCCESS; -} -static int usage_lf_em4x05_wipe(void) { - PrintAndLogEx(NORMAL, "Wipe EM4x05/EM4x69. Tag must be on antenna. "); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Usage: lf em 4x05_wipe [h] "); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h - this help"); - PrintAndLogEx(NORMAL, " c - chip type : 0 em4205"); - PrintAndLogEx(NORMAL, " 1 em4305 (default)"); - PrintAndLogEx(NORMAL, " pwd - password (hex) (optional)"); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " lf em 4x05_wipe"); - PrintAndLogEx(NORMAL, " lf em 4x05_wipe 11223344"); - return PM3_SUCCESS; -} -static int usage_lf_em4x05_read(void) { - PrintAndLogEx(NORMAL, "Read EM4x05/EM4x69. Tag must be on antenna. "); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Usage: lf em 4x05_read [h]
"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h - this help"); - PrintAndLogEx(NORMAL, " address - memory address to read. (0-15)"); - PrintAndLogEx(NORMAL, " pwd - password (hex) (optional)"); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " lf em 4x05_read 1"); - PrintAndLogEx(NORMAL, " lf em 4x05_read 1 11223344"); - return PM3_SUCCESS; -} -static int usage_lf_em4x05_write(void) { - PrintAndLogEx(NORMAL, "Write EM4x05/4x69. Tag must be on antenna. "); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Usage: lf em 4x05_write [h]
"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h - this help"); - PrintAndLogEx(NORMAL, " address - memory address to write to. (0-13, 99 for Protection Words)"); - PrintAndLogEx(NORMAL, " data - data to write (hex)"); - PrintAndLogEx(NORMAL, " pwd - password (hex) (optional)"); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " lf em 4x05_write 1 deadc0de"); - PrintAndLogEx(NORMAL, " lf em 4x05_write 1 deadc0de 11223344"); - return PM3_SUCCESS; -} -static int usage_lf_em4x05_info(void) { - PrintAndLogEx(NORMAL, "Tag information EM4205/4305/4469//4569 tags. Tag must be on antenna."); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Usage: lf em 4x05_info [h] "); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h - this help"); - PrintAndLogEx(NORMAL, " pwd - password (hex) (optional)"); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " lf em 4x05_info"); - PrintAndLogEx(NORMAL, " lf em 4x05_info deadc0de"); - return PM3_SUCCESS; -} - /* Read the ID of an EM410x tag. * Format: * 1111 1111 1 <-- standard non-repeatable header @@ -694,1377 +626,6 @@ static int CmdEM410xClone(const char *Cmd) { return resp.status; } -//**************** Start of EM4x50 Code ************************ - -// even parity COLUMN -static bool EM_ColParityTest(uint8_t *bs, size_t size, uint8_t rows, uint8_t cols, uint8_t pType) { - if (rows * cols > size) return false; - uint8_t colP = 0; - - for (uint8_t c = 0; c < cols - 1; c++) { - for (uint8_t r = 0; r < rows; r++) { - colP ^= bs[(r * cols) + c]; - } - if (colP != pType) return false; - colP = 0; - } - return true; -} - -#define EM_PREAMBLE_LEN 6 -// download samples from device and copy to Graphbuffer -static bool downloadSamplesEM(void) { - - // 8 bit preamble + 32 bit word response (max clock (128) * 40bits = 5120 samples) - uint8_t got[6000]; - if (!GetFromDevice(BIG_BUF, got, sizeof(got), 0, NULL, 0, NULL, 2500, false)) { - PrintAndLogEx(WARNING, "(downloadSamplesEM) command execution time out"); - return false; - } - - setGraphBuf(got, sizeof(got)); - // set signal properties low/high/mean/amplitude and is_noise detection - computeSignalProperties(got, sizeof(got)); - RepaintGraphWindow(); - if (getSignalProperties()->isnoise) { - PrintAndLogEx(DEBUG, "No tag found - signal looks like noise"); - return false; - } - return true; -} - -// em_demod -static int doPreambleSearch(size_t *startIdx) { - - // sanity check - if (DemodBufferLen < EM_PREAMBLE_LEN) { - PrintAndLogEx(DEBUG, "DEBUG: Error - EM4305 demodbuffer too small"); - return PM3_ESOFT; - } - - // set size to 9 to only test first 3 positions for the preamble - // do not set it too long else an error preamble followed by 010 could be seen as success. - size_t size = (9 > DemodBufferLen) ? DemodBufferLen : 9; - *startIdx = 0; - // skip first two 0 bits as they might have been missed in the demod - uint8_t preamble[EM_PREAMBLE_LEN] = {0, 0, 1, 0, 1, 0}; - - if (!preambleSearchEx(DemodBuffer, preamble, EM_PREAMBLE_LEN, &size, startIdx, true)) { - uint8_t errpreamble[EM_PREAMBLE_LEN] = {0, 0, 0, 0, 0, 1}; - if (!preambleSearchEx(DemodBuffer, errpreamble, EM_PREAMBLE_LEN, &size, startIdx, true)) { - PrintAndLogEx(DEBUG, "DEBUG: Error - EM4305 preamble not found :: %zu", *startIdx); - return PM3_ESOFT; - } - return PM3_EFAILED; // Error preamble found - } - return PM3_SUCCESS; -} - -static bool detectFSK(void) { - // detect fsk clock - if (GetFskClock("", false) == 0) { - PrintAndLogEx(DEBUG, "DEBUG: Error - EM: FSK clock failed"); - return false; - } - // demod - int ans = FSKrawDemod(0, 0, 0, 0, false); - if (ans != PM3_SUCCESS) { - PrintAndLogEx(DEBUG, "DEBUG: Error - EM: FSK Demod failed"); - return false; - } - return true; -} -// PSK clocks should be easy to detect ( but difficult to demod a non-repeating pattern... ) -static bool detectPSK(void) { - int ans = GetPskClock("", false); - if (ans <= 0) { - PrintAndLogEx(DEBUG, "DEBUG: Error - EM: PSK clock failed"); - return false; - } - //demod - //try psk1 -- 0 0 6 (six errors?!?) - ans = PSKDemod(0, 0, 6, false); - if (ans != PM3_SUCCESS) { - PrintAndLogEx(DEBUG, "DEBUG: Error - EM: PSK1 Demod failed"); - - //try psk1 inverted - ans = PSKDemod(0, 1, 6, false); - if (ans != PM3_SUCCESS) { - PrintAndLogEx(DEBUG, "DEBUG: Error - EM: PSK1 inverted Demod failed"); - return false; - } - } - // either PSK1 or PSK1 inverted is ok from here. - // lets check PSK2 later. - return true; -} -// try manchester - NOTE: ST only applies to T55x7 tags. -static bool detectASK_MAN(void) { - bool stcheck = false; - if (ASKDemod_ext(0, 0, 50, 0, false, false, false, 1, &stcheck) != PM3_SUCCESS) { - PrintAndLogEx(DEBUG, "DEBUG: Error - EM: ASK/Manchester Demod failed"); - return false; - } - return true; -} - -static bool detectASK_BI(void) { - int ans = ASKbiphaseDemod(0, 0, 1, 50, false); - if (ans != PM3_SUCCESS) { - PrintAndLogEx(DEBUG, "DEBUG: Error - EM: ASK/biphase normal demod failed"); - - ans = ASKbiphaseDemod(0, 1, 1, 50, false); - if (ans != PM3_SUCCESS) { - PrintAndLogEx(DEBUG, "DEBUG: Error - EM: ASK/biphase inverted demod failed"); - return false; - } - } - return true; -} -static bool detectNRZ(void) { - int ans = NRZrawDemod(0, 0, 1, false); - if (ans != PM3_SUCCESS) { - PrintAndLogEx(DEBUG, "DEBUG: Error - EM: NRZ normal demod failed"); - - ans = NRZrawDemod(0, 1, 1, false); - if (ans != PM3_SUCCESS) { - PrintAndLogEx(DEBUG, "DEBUG: Error - EM: NRZ inverted demod failed"); - return false; - } - } - - return true; -} - -// param: idx - start index in demoded data. -static int setDemodBufferEM(uint32_t *word, size_t idx) { - - //test for even parity bits. - uint8_t parity[45] = {0}; - memcpy(parity, DemodBuffer, 45); - if (!EM_ColParityTest(DemodBuffer + idx + EM_PREAMBLE_LEN, 45, 5, 9, 0)) { - PrintAndLogEx(DEBUG, "DEBUG: Error - End Parity check failed"); - return PM3_ESOFT; - } - - // test for even parity bits and remove them. (leave out the end row of parities so 36 bits) - if (!removeParity(DemodBuffer, idx + EM_PREAMBLE_LEN, 9, 0, 36)) { - PrintAndLogEx(DEBUG, "DEBUG: Error - EM, failed removing parity"); - return PM3_ESOFT; - } - setDemodBuff(DemodBuffer, 32, 0); - *word = bytebits_to_byteLSBF(DemodBuffer, 32); - return PM3_SUCCESS; -} - -// FSK, PSK, ASK/MANCHESTER, ASK/BIPHASE, ASK/DIPHASE, NRZ -// should cover 90% of known used configs -// the rest will need to be manually demoded for now... -static int demodEM4x05resp(uint32_t *word, bool onlyPreamble) { - size_t idx = 0; - *word = 0; - bool found_err = false; - int res = PM3_SUCCESS; - do { - if (detectASK_MAN()) { - res = doPreambleSearch(&idx); - if (res == PM3_SUCCESS) - break; - if (res == PM3_EFAILED) - // go on, maybe it's false positive and another modulation will work - found_err = true; - } - if (detectASK_BI()) { - res = doPreambleSearch(&idx); - if (res == PM3_SUCCESS) - break; - if (res == PM3_EFAILED) - found_err = true; - } - if (detectNRZ()) { - res = doPreambleSearch(&idx); - if (res == PM3_SUCCESS) - break; - if (res == PM3_EFAILED) - found_err = true; - } - if (detectFSK()) { - res = doPreambleSearch(&idx); - if (res == PM3_SUCCESS) - break; - if (res == PM3_EFAILED) - found_err = true; - } - if (detectPSK()) { - res = doPreambleSearch(&idx); - if (res == PM3_SUCCESS) - break; - if (res == PM3_EFAILED) - found_err = true; - - psk1TOpsk2(DemodBuffer, DemodBufferLen); - res = doPreambleSearch(&idx); - if (res == PM3_SUCCESS) - break; - if (res == PM3_EFAILED) - found_err = true; - } - if (found_err) - return PM3_EFAILED; - return PM3_ESOFT; - } while (0); - if (onlyPreamble) - return PM3_SUCCESS; - res = setDemodBufferEM(word, idx); - if (res == PM3_SUCCESS) - return res; - if (found_err) - return PM3_EFAILED; - return res; -} - -//////////////// 4205 / 4305 commands -#include "util_posix.h" // msclock -int EM4x05ReadWord_ext(uint8_t addr, uint32_t pwd, bool usePwd, uint32_t *word) { - - struct { - uint32_t password; - uint8_t address; - uint8_t usepwd; - } PACKED payload; - - payload.password = pwd; - payload.address = addr; - payload.usepwd = usePwd; - - clearCommandBuffer(); - SendCommandNG(CMD_LF_EM4X_READWORD, (uint8_t *)&payload, sizeof(payload)); - PacketResponseNG resp; - if (!WaitForResponseTimeout(CMD_LF_EM4X_READWORD, &resp, 10000)) { - PrintAndLogEx(WARNING, "(EM4x05ReadWord_ext) timeout while waiting for reply."); - return PM3_ETIMEOUT; - } - - if (downloadSamplesEM() == false) { - return PM3_ESOFT; - } - return demodEM4x05resp(word, false); -} - -static int CmdEM4x05Demod(const char *Cmd) { -// uint8_t ctmp = tolower(param_getchar(Cmd, 0)); -// if (ctmp == 'h') return usage_lf_em4x05_demod(); - uint32_t dummy = 0; - return demodEM4x05resp(&dummy, false); -} - -static int CmdEM4x05Dump(const char *Cmd) { - uint8_t addr = 0; - uint32_t pwd = 0; - bool usePwd = false; - bool needReadPwd = true; - uint8_t cmdp = 0; - uint8_t bytes[4] = {0}; - uint32_t data[16]; - char preferredName[FILE_PATH_SIZE] = {0}; - char optchk[10]; - - while (param_getchar(Cmd, cmdp) != 0x00) { - switch (tolower(param_getchar(Cmd, cmdp))) { - case 'h': - return usage_lf_em4x05_dump(); - break; - case 'f': // since f could match in password, lets confirm it is 1 character only for an option - param_getstr(Cmd, cmdp, optchk, sizeof(optchk)); - if (strlen(optchk) == 1) { // Have a single character f so filename no password - param_getstr(Cmd, cmdp + 1, preferredName, FILE_PATH_SIZE); - cmdp += 2; - break; - } // if not a single 'f' dont break and flow onto default as should be password - - default : // for backwards-compatibility options should be > 'f' else assume its the hex password` - // for now use default input of 1 as invalid (unlikely 1 will be a valid password...) - pwd = param_get32ex(Cmd, cmdp, 1, 16); - if (pwd != 1) - usePwd = true; - cmdp++; - }; - } - - int success = PM3_SUCCESS; - int status, status14, status15; - uint32_t lock_bits = 0x00; // no blocks locked - bool gotLockBits = false; - bool lockInPW2 = false; - uint32_t word = 0; - const char *info[] = {"Info/User", "UID", "Password", "User", "Config", "User", "User", "User", "User", "User", "User", "User", "User", "User", "Lock", "Lock"}; - - if (usePwd) { - // Test first if a password is required - status = EM4x05ReadWord_ext(14, pwd, false, &word); - if (status == PM3_SUCCESS) { - PrintAndLogEx(INFO, "Note that password doesn't seem to be needed"); - needReadPwd = false; - } - } - PrintAndLogEx(NORMAL, "Addr | data | ascii |lck| info"); - PrintAndLogEx(NORMAL, "-----+----------+-------+---+-----"); - - // To flag any blocks locked we need to read blocks 14 and 15 first - // dont swap endin until we get block lock flags. - status14 = EM4x05ReadWord_ext(14, pwd, usePwd, &word); - if (status14 == PM3_SUCCESS) { - if (!usePwd) - needReadPwd = false; - if ((word & 0x00008000) != 0x00) { - lock_bits = word; - gotLockBits = true; - } - data[14] = word; - } else { - success = PM3_ESOFT; // If any error ensure fail is set so not to save invalid data - } - status15 = EM4x05ReadWord_ext(15, pwd, usePwd, &word); - if (status15 == PM3_SUCCESS) { - if ((word & 0x00008000) != 0x00) { // assume block 15 is the current lock block - lock_bits = word; - gotLockBits = true; - lockInPW2 = true; - } - data[15] = word; - } else { - success = PM3_ESOFT; // If any error ensure fail is set so not to save invalid data - } - uint32_t lockbit; - // Now read blocks 0 - 13 as we have 14 and 15 - for (; addr < 14; addr++) { - lockbit = (lock_bits >> addr) & 1; - if (addr == 2) { - if (usePwd) { - if ((needReadPwd) && (success != PM3_ESOFT)) { - data[addr] = BSWAP_32(pwd); - num_to_bytes(pwd, 4, bytes); - PrintAndLogEx(NORMAL, " %02u | %08X | %s | %s | %s", addr, pwd, sprint_ascii(bytes, 4), gotLockBits ? (lockbit ? _RED_("x") : " ") : _YELLOW_("?"), info[addr]); - } else { - // The pwd is not needed for Login so we're not sure what's the actual content of that block - PrintAndLogEx(NORMAL, " %02u | | | | %-10s " _YELLOW_("write only"), addr, info[addr]); - } - } else { - data[addr] = 0x00; // Unknown password, but not used to set to zeros - PrintAndLogEx(NORMAL, " %02u | | | | %-10s " _YELLOW_("write only"), addr, info[addr]); - } - } else { - // success &= EM4x05ReadWord_ext(addr, pwd, usePwd, &word); - status = EM4x05ReadWord_ext(addr, pwd, usePwd, &word); // Get status for single read - if (status != PM3_SUCCESS) - success = PM3_ESOFT; // If any error ensure fail is set so not to save invalid data - data[addr] = BSWAP_32(word); - if (status == PM3_SUCCESS) { - num_to_bytes(word, 4, bytes); - PrintAndLogEx(NORMAL, " %02u | %08X | %s | %s | %s", addr, word, sprint_ascii(bytes, 4), gotLockBits ? (lockbit ? _RED_("x") : " ") : _YELLOW_("?"), info[addr]); - } else - PrintAndLogEx(NORMAL, " %02u | | | | %-10s %s", addr, info[addr], status == PM3_EFAILED ? _RED_("read denied") : _RED_("read failed")); - } - } - // Print blocks 14 and 15 - // Both lock bits are protected with bit idx 14 (special case) - addr = 14; - if (status14 == PM3_SUCCESS) { - lockbit = (lock_bits >> addr) & 1; - PrintAndLogEx(NORMAL, " %02u | %08X | %s | %s | %-10s %s", addr, data[addr], sprint_ascii(bytes, 4), gotLockBits ? (lockbit ? _RED_("x") : " ") : _YELLOW_("?"), info[addr], lockInPW2 ? "" : _GREEN_("active")); - } else { - PrintAndLogEx(NORMAL, " %02u | | | | %-10s %s", addr, info[addr], status14 == PM3_EFAILED ? _RED_("read denied") : _RED_("read failed")); - } - addr = 15; - if (status15 == PM3_SUCCESS) { - lockbit = (lock_bits >> 14) & 1; // beware lock bit of word15 is pr14 - PrintAndLogEx(NORMAL, " %02u | %08X | %s | %s | %-10s %s", addr, data[addr], sprint_ascii(bytes, 4), gotLockBits ? (lockbit ? _RED_("x") : " ") : _YELLOW_("?"), info[addr], lockInPW2 ? _GREEN_("active") : ""); - } else { - PrintAndLogEx(NORMAL, " %02u | | | | %-10s %s", addr, info[addr], status15 == PM3_EFAILED ? _RED_("read denied") : _RED_("read failed")); - } - // Update endian for files - data[14] = BSWAP_32(data[14]); - data[15] = BSWAP_32(data[15]); - - if (success == PM3_SUCCESS) { // all ok save dump to file - // saveFileEML will add .eml extension to filename - // saveFile (binary) passes in the .bin extension. - if (strcmp(preferredName, "") == 0) // Set default filename, if not set by user - sprintf(preferredName, "lf-4x05-%08X-dump", BSWAP_32(data[1])); - - saveFileEML(preferredName, (uint8_t *)data, 16 * sizeof(uint32_t), sizeof(uint32_t)); - saveFile(preferredName, ".bin", data, sizeof(data)); - } - - return success; -} - -static int CmdEM4x05Read(const char *Cmd) { - uint8_t addr; - uint32_t pwd; - bool usePwd = false; - - uint8_t ctmp = tolower(param_getchar(Cmd, 0)); - if (strlen(Cmd) == 0 || ctmp == 'h') return usage_lf_em4x05_read(); - - addr = param_get8ex(Cmd, 0, 50, 10); - pwd = param_get32ex(Cmd, 1, 0xFFFFFFFF, 16); - - if (addr > 15) { - PrintAndLogEx(WARNING, "Address must be between 0 and 15"); - return PM3_ESOFT; - } - if (pwd == 0xFFFFFFFF) { - PrintAndLogEx(INFO, "Reading address %02u", addr); - } else { - usePwd = true; - PrintAndLogEx(INFO, "Reading address %02u using password %08X", addr, pwd); - } - - uint32_t word = 0; - int status = EM4x05ReadWord_ext(addr, pwd, usePwd, &word); - if (status == PM3_SUCCESS) - PrintAndLogEx(SUCCESS, "Address %02d | %08X - %s", addr, word, (addr > 13) ? "Lock" : ""); - else if (status == PM3_EFAILED) - PrintAndLogEx(ERR, "Tag denied Read operation"); - else - PrintAndLogEx(WARNING, "No answer from tag"); - return status; -} - -static int CmdEM4x05Write(const char *Cmd) { - uint8_t ctmp = tolower(param_getchar(Cmd, 0)); - if (strlen(Cmd) == 0 || ctmp == 'h') return usage_lf_em4x05_write(); - - bool usePwd = false; - uint8_t addr; - uint32_t data, pwd; - - addr = param_get8ex(Cmd, 0, 50, 10); - data = param_get32ex(Cmd, 1, 0, 16); - pwd = param_get32ex(Cmd, 2, 0xFFFFFFFF, 16); - bool protectOperation = addr == 99; // will do better with cliparser... - - if ((addr > 13) && (!protectOperation)) { - PrintAndLogEx(WARNING, "Address must be between 0 and 13"); - return PM3_EINVARG; - } - if (pwd == 0xFFFFFFFF) { - if (protectOperation) - PrintAndLogEx(INFO, "Writing protection words data %08X", data); - else - PrintAndLogEx(INFO, "Writing address %d data %08X", addr, data); - } else { - usePwd = true; - if (protectOperation) - PrintAndLogEx(INFO, "Writing protection words data %08X using password %08X", data, pwd); - else - PrintAndLogEx(INFO, "Writing address %d data %08X using password %08X", addr, data, pwd); - } - - if (protectOperation) { // set Protect Words - struct { - uint32_t password; - uint32_t data; - uint8_t usepwd; - } PACKED payload; - - payload.password = pwd; - payload.data = data; - payload.usepwd = usePwd; - - clearCommandBuffer(); - SendCommandNG(CMD_LF_EM4X_PROTECTWORD, (uint8_t *)&payload, sizeof(payload)); - PacketResponseNG resp; - if (!WaitForResponseTimeout(CMD_LF_EM4X_PROTECTWORD, &resp, 2000)) { - PrintAndLogEx(ERR, "Error occurred, device did not respond during write operation."); - return PM3_ETIMEOUT; - } - } else { - struct { - uint32_t password; - uint32_t data; - uint8_t address; - uint8_t usepwd; - } PACKED payload; - - payload.password = pwd; - payload.data = data; - payload.address = addr; - payload.usepwd = usePwd; - - clearCommandBuffer(); - SendCommandNG(CMD_LF_EM4X_WRITEWORD, (uint8_t *)&payload, sizeof(payload)); - PacketResponseNG resp; - if (!WaitForResponseTimeout(CMD_LF_EM4X_WRITEWORD, &resp, 2000)) { - PrintAndLogEx(ERR, "Error occurred, device did not respond during write operation."); - return PM3_ETIMEOUT; - } - } - if (!downloadSamplesEM()) - return PM3_ENODATA; - - uint32_t dummy = 0; - int status = demodEM4x05resp(&dummy, true); - if (status == PM3_SUCCESS) - PrintAndLogEx(SUCCESS, "Success writing to tag"); - else if (status == PM3_EFAILED) - PrintAndLogEx(ERR, "Tag denied %s operation", protectOperation ? "Protect" : "Write"); - else - PrintAndLogEx(DEBUG, "No answer from tag"); - - PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf em 4x05_read`") " to verify"); - return status; -} -static int CmdEM4x05Wipe(const char *Cmd) { - uint8_t addr = 0; - uint32_t pwd = 0; - uint8_t cmdp = 0; - uint8_t chipType = 1; // em4305 - uint32_t chipInfo = 0x00040072; // Chip info/User Block normal 4305 Chip Type - uint32_t chipUID = 0x614739AE; // UID normally readonly, but just in case - uint32_t blockData = 0x00000000; // UserBlock/Password (set to 0x00000000 for a wiped card1 - uint32_t config = 0x0001805F; // Default config (no password) - int success = PM3_SUCCESS; - char cmdStr [100]; - char optchk[10]; - - while (param_getchar(Cmd, cmdp) != 0x00) { - // check if cmd is a 1 byte option - param_getstr(Cmd, cmdp, optchk, sizeof(optchk)); - if (strlen(optchk) == 1) { // Have a single character so option not part of password - switch (tolower(param_getchar(Cmd, cmdp))) { - case 'c': // chip type - if (param_getchar(Cmd, cmdp) != 0x00) - chipType = param_get8ex(Cmd, cmdp + 1, 0, 10); - cmdp += 2; - break; - case 'h': // return usage_lf_em4x05_wipe(); - default : // Unknown or 'h' send help - return usage_lf_em4x05_wipe(); - break; - }; - } else { // Not a single character so assume password - pwd = param_get32ex(Cmd, cmdp, 1, 16); - cmdp++; - } - } - - switch (chipType) { - case 0 : // em4205 - chipInfo = 0x00040070; - config = 0x0001805F; - break; - case 1 : // em4305 - chipInfo = 0x00040072; - config = 0x0001805F; - break; - default : // Type 0/Default : EM4305 - chipInfo = 0x00040072; - config = 0x0001805F; - } - - // block 0 : User Data or Chip Info - sprintf(cmdStr, "%d %08X %08X", 0, chipInfo, pwd); - CmdEM4x05Write(cmdStr); - // block 1 : UID - this should be read only for EM4205 and EM4305 not sure about others - sprintf(cmdStr, "%d %08X %08X", 1, chipUID, pwd); - CmdEM4x05Write(cmdStr); - // block 2 : password - sprintf(cmdStr, "%d %08X %08X", 2, blockData, pwd); - CmdEM4x05Write(cmdStr); - pwd = blockData; // Password should now have changed, so use new password - // block 3 : user data - sprintf(cmdStr, "%d %08X %08X", 3, blockData, pwd); - CmdEM4x05Write(cmdStr); - // block 4 : config - sprintf(cmdStr, "%d %08X %08X", 4, config, pwd); - CmdEM4x05Write(cmdStr); - - // Remainder of user/data blocks - for (addr = 5; addr < 14; addr++) {// Clear user data blocks - sprintf(cmdStr, "%d %08X %08X", addr, blockData, pwd); - CmdEM4x05Write(cmdStr); - } - - return success; -} - -static void printEM4x05config(uint32_t wordData) { - uint16_t datarate = (((wordData & 0x3F) + 1) * 2); - uint8_t encoder = ((wordData >> 6) & 0xF); - char enc[14]; - memset(enc, 0, sizeof(enc)); - - uint8_t PSKcf = (wordData >> 10) & 0x3; - char cf[10]; - memset(cf, 0, sizeof(cf)); - uint8_t delay = (wordData >> 12) & 0x3; - char cdelay[33]; - memset(cdelay, 0, sizeof(cdelay)); - uint8_t numblks = EM4x05_GET_NUM_BLOCKS(wordData); - uint8_t LWR = numblks + 5 - 1; //last word read - switch (encoder) { - case 0: - snprintf(enc, sizeof(enc), "NRZ"); - break; - case 1: - snprintf(enc, sizeof(enc), "Manchester"); - break; - case 2: - snprintf(enc, sizeof(enc), "Biphase"); - break; - case 3: - snprintf(enc, sizeof(enc), "Miller"); - break; - case 4: - snprintf(enc, sizeof(enc), "PSK1"); - break; - case 5: - snprintf(enc, sizeof(enc), "PSK2"); - break; - case 6: - snprintf(enc, sizeof(enc), "PSK3"); - break; - case 7: - snprintf(enc, sizeof(enc), "Unknown"); - break; - case 8: - snprintf(enc, sizeof(enc), "FSK1"); - break; - case 9: - snprintf(enc, sizeof(enc), "FSK2"); - break; - default: - snprintf(enc, sizeof(enc), "Unknown"); - break; - } - - switch (PSKcf) { - case 0: - snprintf(cf, sizeof(cf), "RF/2"); - break; - case 1: - snprintf(cf, sizeof(cf), "RF/8"); - break; - case 2: - snprintf(cf, sizeof(cf), "RF/4"); - break; - case 3: - snprintf(cf, sizeof(cf), "unknown"); - break; - } - - switch (delay) { - case 0: - snprintf(cdelay, sizeof(cdelay), "no delay"); - break; - case 1: - snprintf(cdelay, sizeof(cdelay), "BP/8 or 1/8th bit period delay"); - break; - case 2: - snprintf(cdelay, sizeof(cdelay), "BP/4 or 1/4th bit period delay"); - break; - case 3: - snprintf(cdelay, sizeof(cdelay), "no delay"); - break; - } - uint8_t readLogin = (wordData & EM4x05_READ_LOGIN_REQ) >> 18; - uint8_t readHKL = (wordData & EM4x05_READ_HK_LOGIN_REQ) >> 19; - uint8_t writeLogin = (wordData & EM4x05_WRITE_LOGIN_REQ) >> 20; - uint8_t writeHKL = (wordData & EM4x05_WRITE_HK_LOGIN_REQ) >> 21; - uint8_t raw = (wordData & EM4x05_READ_AFTER_WRITE) >> 22; - uint8_t disable = (wordData & EM4x05_DISABLE_ALLOWED) >> 23; - uint8_t rtf = (wordData & EM4x05_READER_TALK_FIRST) >> 24; - uint8_t pigeon = (wordData & (1 << 26)) >> 26; - - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, "--- " _CYAN_("Config Information") " ---------------------------"); - PrintAndLogEx(INFO, "ConfigWord: %08X (Word 4)", wordData); - PrintAndLogEx(INFO, " Data Rate: %02u | "_YELLOW_("RF/%u"), wordData & 0x3F, datarate); - PrintAndLogEx(INFO, " Encoder: %u | " _YELLOW_("%s"), encoder, enc); - PrintAndLogEx(INFO, " PSK CF: %u | %s", PSKcf, cf); - PrintAndLogEx(INFO, " Delay: %u | %s", delay, cdelay); - PrintAndLogEx(INFO, " LastWordR: %02u | Address of last word for default read - meaning %u blocks are output", LWR, numblks); - PrintAndLogEx(INFO, " ReadLogin: %u | Read login is %s", readLogin, readLogin ? _YELLOW_("required") : _GREEN_("not required")); - PrintAndLogEx(INFO, " ReadHKL: %u | Read housekeeping words login is %s", readHKL, readHKL ? _YELLOW_("required") : _GREEN_("not required")); - PrintAndLogEx(INFO, "WriteLogin: %u | Write login is %s", writeLogin, writeLogin ? _YELLOW_("required") : _GREEN_("not required")); - PrintAndLogEx(INFO, " WriteHKL: %u | Write housekeeping words login is %s", writeHKL, writeHKL ? _YELLOW_("required") : _GREEN_("not Required")); - PrintAndLogEx(INFO, " R.A.W.: %u | Read after write is %s", raw, raw ? "on" : "off"); - PrintAndLogEx(INFO, " Disable: %u | Disable command is %s", disable, disable ? "accepted" : "not accepted"); - PrintAndLogEx(INFO, " R.T.F.: %u | Reader talk first is %s", rtf, rtf ? _YELLOW_("enabled") : "disabled"); - PrintAndLogEx(INFO, " Pigeon: %u | Pigeon mode is %s", pigeon, pigeon ? _YELLOW_("enabled") : "disabled"); -} - -static void printEM4x05info(uint32_t block0, uint32_t serial) { - - uint8_t chipType = (block0 >> 1) & 0xF; - uint8_t cap = (block0 >> 5) & 3; - uint16_t custCode = (block0 >> 9) & 0x2FF; - - PrintAndLogEx(INFO, " block0: %X", block0); - PrintAndLogEx(INFO, " chiptype: %X", chipType); - PrintAndLogEx(INFO, "capacitor: %X", cap); - PrintAndLogEx(INFO, " custcode: %X", custCode); - - /* bits - // 0, rfu - // 1,2,3,4 chip type - // 5,6 resonant cap - // 7,8, rfu - // 9 - 18 customer code - // 19, rfu - - 98765432109876543210 - 001000000000 - // 00100000000001111000 - xxx---- - // 1100 - // 011 - // 00100000000 - */ - - PrintAndLogEx(INFO, "--- " _CYAN_("Tag Information") " ---------------------------"); - - char ctstr[50]; - snprintf(ctstr, sizeof(ctstr), " Chip Type: %u | ", chipType); - switch (chipType) { - case 9: - snprintf(ctstr + strlen(ctstr), sizeof(ctstr) - strlen(ctstr), _YELLOW_("%s"), "EM4305"); - break; - case 4: - snprintf(ctstr + strlen(ctstr), sizeof(ctstr) - strlen(ctstr), _YELLOW_("%s"), "EM4469"); - break; - case 8: - snprintf(ctstr + strlen(ctstr), sizeof(ctstr) - strlen(ctstr), _YELLOW_("%s"), "EM4205"); - break; - //add more here when known - default: - snprintf(ctstr + strlen(ctstr), sizeof(ctstr) - strlen(ctstr), _YELLOW_("%s"), "Unknown"); - break; - } - PrintAndLogEx(SUCCESS, "%s", ctstr); - - switch (cap) { - case 3: - PrintAndLogEx(SUCCESS, " Cap Type: %u | 330pF", cap); - break; - case 2: - PrintAndLogEx(SUCCESS, " Cap Type: %u | %spF", cap, (chipType == 2) ? "75" : "210"); - break; - case 1: - PrintAndLogEx(SUCCESS, " Cap Type: %u | 250pF", cap); - break; - case 0: - PrintAndLogEx(SUCCESS, " Cap Type: %u | no resonant capacitor", cap); - break; - default: - PrintAndLogEx(SUCCESS, " Cap Type: %u | unknown", cap); - break; - } - - PrintAndLogEx(SUCCESS, " Cust Code: %03u | %s", custCode, (custCode == 0x200) ? "Default" : "Unknown"); - if (serial != 0) - PrintAndLogEx(SUCCESS, " Serial #: " _YELLOW_("%08X"), serial); -} - -static void printEM4x05ProtectionBits(uint32_t word, uint8_t addr) { - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, "--- " _CYAN_("Protection") " ---------------------------"); - PrintAndLogEx(INFO, "ProtectionWord: %08X (Word %i)", word, addr); - for (uint8_t i = 0; i < 15; i++) { - PrintAndLogEx(INFO, " Word: %02u | %s", i, ((1 << i) & word) ? _RED_("write Locked") : "unlocked"); - if (i == 14) - PrintAndLogEx(INFO, " Word: %02u | %s", i + 1, ((1 << i) & word) ? _RED_("write locked") : "unlocked"); - } -} - -//quick test for EM4x05/EM4x69 tag -bool EM4x05IsBlock0(uint32_t *word) { - return (EM4x05ReadWord_ext(0, 0, false, word) == PM3_SUCCESS); -} - -static int CmdEM4x05Info(const char *Cmd) { -#define EM_SERIAL_BLOCK 1 -#define EM_CONFIG_BLOCK 4 -#define EM_PROT1_BLOCK 14 -#define EM_PROT2_BLOCK 15 - uint32_t pwd; - uint32_t word = 0, block0 = 0, serial = 0; - bool usePwd = false; - uint8_t ctmp = tolower(param_getchar(Cmd, 0)); - if (ctmp == 'h') return usage_lf_em4x05_info(); - - // for now use default input of 1 as invalid (unlikely 1 will be a valid password...) - pwd = param_get32ex(Cmd, 0, 0xFFFFFFFF, 16); - - if (pwd != 0xFFFFFFFF) - usePwd = true; - - // read word 0 (chip info) - // block 0 can be read even without a password. - if (EM4x05IsBlock0(&block0) == false) - return PM3_ESOFT; - - // read word 1 (serial #) doesn't need pwd - // continue if failed, .. non blocking fail. - EM4x05ReadWord_ext(EM_SERIAL_BLOCK, 0, false, &serial); - printEM4x05info(block0, serial); - - // read word 4 (config block) - // needs password if one is set - if (EM4x05ReadWord_ext(EM_CONFIG_BLOCK, pwd, usePwd, &word) != PM3_SUCCESS) - return PM3_ESOFT; - - printEM4x05config(word); - - // read word 14 and 15 to see which is being used for the protection bits - if (EM4x05ReadWord_ext(EM_PROT1_BLOCK, pwd, usePwd, &word) != PM3_SUCCESS) { - return PM3_ESOFT; - } - if (word & 0x8000) { - printEM4x05ProtectionBits(word, EM_PROT1_BLOCK); - return PM3_SUCCESS; - } else { // if status bit says this is not the used protection word - if (EM4x05ReadWord_ext(EM_PROT2_BLOCK, pwd, usePwd, &word) != PM3_SUCCESS) - return PM3_ESOFT; - if (word & 0x8000) { - printEM4x05ProtectionBits(word, EM_PROT2_BLOCK); - return PM3_SUCCESS; - } - } - //something went wrong - return PM3_ESOFT; -} - -static bool is_cancelled(void) { - if (kbd_enter_pressed()) { - PrintAndLogEx(WARNING, "\naborted via keyboard!\n"); - return true; - } - return false; -} -// load a default pwd file. -static int CmdEM4x05Chk(const char *Cmd) { - - CLIParserContext *ctx; - CLIParserInit(&ctx, "lf em 4x05_chk", - "This command uses a dictionary attack against EM4205/4305/4469/4569", - "lf em 4x05_chk\n" - "lf em 4x05_chk -e 0x00000022B8 -> remember to use 0x for hex\n" - "lf em 4x05_chk -f t55xx_default_pwds -> use T55xx default dictionary" - ); - - void *argtable[] = { - arg_param_begin, - arg_strx0("f", "file", "<*.dic>", "loads a default keys dictionary file <*.dic>"), - arg_u64_0("e", "em", "", "try the calculated password from some cloners based on EM4100 ID"), - arg_param_end - }; - CLIExecWithReturn(ctx, Cmd, argtable, true); - int fnlen = 0; - char filename[FILE_PATH_SIZE] = {0}; - CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); - uint64_t card_id = arg_get_u64_def(ctx, 2, 0); - CLIParserFree(ctx); - - if (strlen(filename) == 0) { - snprintf(filename, sizeof(filename), "t55xx_default_pwds"); - } - PrintAndLogEx(NORMAL, ""); - - uint8_t addr = 4; - uint32_t word = 0; - bool found = false; - uint64_t t1 = msclock(); - - // White cloner password based on EM4100 ID - if ( card_id > 0 ) { - - uint32_t pwd = lf_t55xx_white_pwdgen(card_id & 0xFFFFFFFF); - PrintAndLogEx(INFO, "testing %08"PRIX32" generated ", pwd); - - int status = EM4x05ReadWord_ext(addr, pwd, true, &word); - if (status == PM3_SUCCESS) { - PrintAndLogEx(SUCCESS, "found valid password [ " _GREEN_("%08"PRIX32) " ]", pwd); - found = true; - } - } - - // Loop dictionary - uint8_t *keyBlock = NULL; - if (found == false) { - - PrintAndLogEx(INFO, "press " _YELLOW_("'enter'") " to cancel the command"); - - word = 0; - uint32_t keycount = 0; - - int res = loadFileDICTIONARY_safe(filename, (void **) &keyBlock, 4, &keycount); - if (res != PM3_SUCCESS || keycount == 0 || keyBlock == NULL) { - PrintAndLogEx(WARNING, "no keys found in file"); - if (keyBlock != NULL) - free(keyBlock); - - return PM3_ESOFT; - } - - for (uint32_t c = 0; c < keycount; ++c) { - - if (!session.pm3_present) { - PrintAndLogEx(WARNING, "device offline\n"); - free(keyBlock); - return PM3_ENODATA; - } - - if (is_cancelled()) { - free(keyBlock); - return PM3_EOPABORTED; - } - - uint32_t curr_password = bytes_to_num(keyBlock + 4 * c, 4); - - PrintAndLogEx(INFO, "testing %08"PRIX32, curr_password); - - int status = EM4x05ReadWord_ext(addr, curr_password, 1, &word); - if (status == PM3_SUCCESS) { - PrintAndLogEx(SUCCESS, "found valid password [ " _GREEN_("%08"PRIX32) " ]", curr_password); - found = true; - break; - } - } - } - - if (found == false) - PrintAndLogEx(WARNING, "check pwd failed"); - - free(keyBlock); - - t1 = msclock() - t1; - PrintAndLogEx(SUCCESS, "\ntime in check pwd " _YELLOW_("%.0f") " seconds\n", (float)t1 / 1000.0); - return PM3_SUCCESS; -} - -typedef struct { - uint16_t cnt; - uint32_t value; -} em4x05_unlock_item_t; - -static int unlock_write_protect(bool use_pwd, uint32_t pwd, uint32_t data, bool verbose) { - - struct { - uint32_t password; - uint32_t data; - uint8_t usepwd; - } PACKED payload; - - payload.password = pwd; - payload.data = data; - payload.usepwd = use_pwd; - - clearCommandBuffer(); - SendCommandNG(CMD_LF_EM4X_PROTECTWORD, (uint8_t *)&payload, sizeof(payload)); - PacketResponseNG resp; - if (WaitForResponseTimeout(CMD_LF_EM4X_PROTECTWORD, &resp, 2000) == false) { - PrintAndLogEx(ERR, "Error occurred, device did not respond during write operation."); - return PM3_ETIMEOUT; - } - - if (!downloadSamplesEM()) - return PM3_ENODATA; - - uint32_t dummy = 0; - int status = demodEM4x05resp(&dummy, true); - if (status == PM3_SUCCESS && verbose) - PrintAndLogEx(SUCCESS, "Success writing to tag"); - else if (status == PM3_EFAILED) - PrintAndLogEx(ERR, "Tag denied PROTECT operation"); - else - PrintAndLogEx(DEBUG, "No answer from tag"); - - return status; -} -static int unlock_reset(bool use_pwd, uint32_t pwd, uint32_t data, bool verbose) { - if (verbose) - PrintAndLogEx(INFO, "resetting the " _RED_("active") " lock block"); - - return unlock_write_protect(use_pwd, pwd, data, false); -} -static void unlock_add_item(em4x05_unlock_item_t *array, uint8_t len, uint32_t value) { - - uint8_t i = 0; - for (; i < len; i++) { - if ( array[i].value == value ) { - array[i].cnt++; - break; - } - if ( array[i].cnt == 0 ) { - array[i].cnt++; - array[i].value = value; - break; - } - } -} - -static int CmdEM4x05Unlock(const char *Cmd) { - - CLIParserContext *ctx; - CLIParserInit(&ctx, "lf em 4x05_unlock", - "execute tear off against EM4205/4305/4469/4569", - "lf em 4x05_unlock\n" - "lf em 4x05_unlock -s 4100 -e 4100 -> lock on and autotune at 4100us\n" - "lf em 4x05_unlock -n 10 -s 3000 -e 4400 -> scan delays 3000us -> 4400us" - ); - - void *argtable[] = { - arg_param_begin, - arg_int0("n", NULL, NULL, "steps to skip"), - arg_int0("s", "start", "", "start scan from delay (us)"), - arg_int0("e", "end", "", "end scan at delay (us)"), - arg_u64_0("p", "pwd", "", "password (0x00000000)"), - arg_lit0("v", "verbose", "verbose output"), - arg_param_end - }; - CLIExecWithReturn(ctx, Cmd, argtable, true); - double n = (double)arg_get_int_def(ctx, 1, 0); - double start = (double)arg_get_int_def(ctx, 2, 2000); - double end = (double)arg_get_int_def(ctx, 3, 6000); - uint64_t inputpwd = arg_get_u64_def(ctx, 4, 0xFFFFFFFFFFFFFFFF); - bool verbose = arg_get_lit(ctx, 5); - CLIParserFree(ctx); - - if ( start > end ) { - PrintAndLogEx(FAILED, "start delay can\'t be larger than end delay %.0lf vs %.0lf", start, end); - return PM3_EINVARG; - } - - if (session.pm3_present == false) { - PrintAndLogEx(WARNING, "device offline\n"); - return PM3_ENODATA; - } - - bool use_pwd = false; - uint32_t pwd = 0; - if (inputpwd != 0xFFFFFFFFFFFFFFFF) { - use_pwd = true; - pwd = inputpwd & 0xFFFFFFFF; - } - - uint32_t search_value = 0; - uint32_t write_value = 0; - // - // inital phase - // - // read word 14 - uint32_t init_14 = 0; - int res = EM4x05ReadWord_ext(14, pwd, use_pwd, &init_14); - if (res != PM3_SUCCESS) { - PrintAndLogEx(FAILED, "failed to read word 14\n"); - return PM3_ENODATA; - } - - - // read 15 - uint32_t init_15 = 0; - res = EM4x05ReadWord_ext(15, pwd, use_pwd, &init_15); - if (res != PM3_SUCCESS) { - PrintAndLogEx(FAILED, "failed to read word 15\n"); - return PM3_ENODATA; - } - -#define ACTIVE_MASK 0x00008000 - if ((init_15 & ACTIVE_MASK) == ACTIVE_MASK) { - search_value = init_15; - } else { - search_value = init_14; - } - - if (search_value == ACTIVE_MASK) { - PrintAndLogEx(SUCCESS, "Tag already fully unlocked, nothing to do"); - return PM3_SUCCESS; - } - - bool my_auto = false; - if (n == 0) { - my_auto = true; - n = (end - start) / 2; - } - - // fix at one specific delay - if (start == end) { - n = 0; - } - - PrintAndLogEx(INFO, "--------------- " _CYAN_("EM4x05 tear-off : target PROTECT") " -----------------------\n"); - - PrintAndLogEx(INFO, "initial prot 14&15 [ " _GREEN_("%08X") ", " _GREEN_("%08X") " ]", init_14, init_15); - - if (use_pwd) { - PrintAndLogEx(INFO, " target password [ " _GREEN_("%08X") " ]", pwd); - } - if (my_auto) { - PrintAndLogEx(INFO, " automatic mode [ " _GREEN_("enabled") " ]"); - } - - PrintAndLogEx(INFO, " target stepping [ " _GREEN_("%.0lf") " ]", n); - PrintAndLogEx(INFO, "target delay range [ " _GREEN_("%.0lf") " ... " _GREEN_("%.0lf") " ]", start, end); - PrintAndLogEx(INFO, " search value [ " _GREEN_("%08X") " ]", search_value); - PrintAndLogEx(INFO, " write value [ " _GREEN_("%08X") " ]", write_value); - - PrintAndLogEx(INFO, "----------------------------------------------------------------------------\n"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, "press " _YELLOW_("'enter'") " to cancel the command"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, "--------------- " _CYAN_("start") " -----------------------\n"); - - int exit_code = PM3_SUCCESS; - uint32_t word14 = 0, word15 = 0; - uint32_t word14b = 0, word15b = 0; - uint32_t tries = 0; - uint32_t soon = 0; - uint32_t late = 0; - - em4x05_unlock_item_t flipped[64]; - - // - // main loop - // - bool success = false; - uint64_t t1 = msclock(); - while (start <= end) { - - if (my_auto && n < 1) { - PrintAndLogEx(INFO, "Reached n < 1 => " _YELLOW_("disabling automatic mode")); - end = start; - my_auto = false; - n = 0; - } - - if (my_auto == false) { - start += n; - } - - if (tries >= 5 && n == 0 && soon != late) { - - if (soon > late) { - PrintAndLogEx(INFO, "Tried %d times, soon:%i late:%i => " _CYAN_("adjust +1 us >> %.0lf us"), tries, soon, late, start); - start++; - end++; - } else { - PrintAndLogEx(INFO, "Tried %d times, soon:%i late:%i => " _CYAN_("adjust -1 us >> %.0lf us"), tries, soon, late, start); - start--; - end--; - } - tries = 0; - soon = 0; - late = 0; - } - - if (is_cancelled()) { - exit_code = PM3_EOPABORTED; - break; - } - - // set tear off trigger - clearCommandBuffer(); - tearoff_params_t params = { - .delay_us = start, - .on = true, - .off = false - }; - res = handle_tearoff(¶ms, verbose); - if ( res != PM3_SUCCESS ) { - PrintAndLogEx(WARNING, "failed to configure tear off"); - return PM3_ESOFT; - } - - // write - res = unlock_write_protect(use_pwd, pwd, write_value, verbose); - - // read after trigger - res = EM4x05ReadWord_ext(14, pwd, use_pwd, &word14); - if (res != PM3_SUCCESS) { - PrintAndLogEx(WARNING, "failed to read 14"); - return PM3_ESOFT; - } - - // read after trigger - res = EM4x05ReadWord_ext(15, pwd, use_pwd, &word15); - if (res != PM3_SUCCESS) { - PrintAndLogEx(WARNING, "failed to read 15"); - return PM3_ESOFT; - } - - if (verbose) - PrintAndLogEx(INFO, "ref:%08X 14:%08X 15:%08X ", search_value, word14, word15); - - if ( word14 == search_value && word15 == 0) { - PrintAndLogEx(INFO, "Status: Nothing happened => " _GREEN_("tearing too soon")); - - if (my_auto) { - start += n; - PrintAndLogEx(INFO, " => " _CYAN_("adjust +%.0lf us >> %.0lf us"), n, start); - n /= 2; - } else { - soon++; - } - } else { - - if (word15 == search_value) { - - if (word14 == 0) { - PrintAndLogEx(INFO, "Status: Protect succeeded => " _GREEN_("tearing too late")); - } else { - if ( word14 == search_value) { - PrintAndLogEx(INFO, "Status: 15 ok, 14 not yet erased => " _GREEN_("tearing too late")); - } else { - PrintAndLogEx(INFO, "Status: 15 ok, 14 partially erased => " _GREEN_("tearing too late")); - } - } - - unlock_reset(use_pwd, pwd, write_value, verbose); - - // read after reset - res = EM4x05ReadWord_ext(14, pwd, use_pwd, &word14b); - if (res != PM3_SUCCESS) { - PrintAndLogEx(WARNING, "failed to read 14"); - return PM3_ESOFT; - } - - if (word14b == 0) { - - unlock_reset(use_pwd, pwd, write_value, verbose); - - res = EM4x05ReadWord_ext(14, pwd, use_pwd, &word14b); - if (res != PM3_SUCCESS) { - PrintAndLogEx(WARNING, "failed to read 14"); - return PM3_ESOFT; - } - } - - if (word14b != search_value) { - - res = EM4x05ReadWord_ext(15, pwd, use_pwd, &word15b); - if (res == PM3_SUCCESS) { - PrintAndLogEx(INFO, "Status: new definitive value! => " _RED_("SUCCESS:") " 14: " _CYAN_("%08X") " 15: %08X", word14b, word15b); - success = true; - break; - } else { - PrintAndLogEx(WARNING, "failed to read 15"); - return PM3_ESOFT; - } - } - if (my_auto) { - end = start; - start -= n; - PrintAndLogEx(INFO, " => " _CYAN_("adjust -%.0lf us >> %.0lf us"), n, start); - n /= 2; - } else { - late++; - } - - } else { - - if (( word15 & ACTIVE_MASK) == ACTIVE_MASK) { - - PrintAndLogEx(INFO, "Status: 15 bitflipped and active => " _RED_("SUCCESS?: ") "14: %08X 15: " _CYAN_("%08X"), word14, word15); - PrintAndLogEx(INFO, "Committing results..."); - - unlock_reset(use_pwd, pwd, write_value, verbose); - - // read after reset - res = EM4x05ReadWord_ext(14, pwd, use_pwd, &word14b); - if ( res != PM3_SUCCESS ) { - PrintAndLogEx(WARNING, "failed to read 14"); - return PM3_ESOFT; - } - - res = EM4x05ReadWord_ext(15, pwd, use_pwd, &word15b); - if ( res != PM3_SUCCESS ) { - PrintAndLogEx(WARNING, "failed to read 15"); - return PM3_ESOFT; - } - - if (verbose) - PrintAndLogEx(INFO, "ref:%08x 14:%08X 15:%08X", search_value, word14b, word15b); - - if ((word14b & ACTIVE_MASK) == ACTIVE_MASK) { - - if (word14b == word15) { - PrintAndLogEx(INFO, "Status: confirmed => " _RED_("SUCCESS: ") "14: " _CYAN_("%08X") " 15: %08X", word14b, word15b); - - unlock_add_item(flipped, 64, word14b); - success = true; - break; - } - - if (word14b != search_value) { - PrintAndLogEx(INFO, "Status: new definitive value! => " _RED_("SUCCESS: ") "14: " _CYAN_("%08X") " 15: %08X", word14b, word15b); - - unlock_add_item(flipped, 64, word14b); - success = true; - break; - } - - PrintAndLogEx(INFO, "Status: failed to commit bitflip => " _RED_("FAIL: ") "14: %08X 15: %08X", word14b, word15b); - } - if (my_auto) { - n = 0; - end = start; - } else { - tries = 0; - soon = 0; - late = 0; - } - } else { - PrintAndLogEx(INFO, "Status: 15 bitflipped but inactive => " _YELLOW_("PROMISING: ") "14: %08X 15: " _CYAN_("%08X"), word14, word15); - - unlock_add_item(flipped, 64, word15); - - soon ++; - } - } - } - - if (my_auto == false) { - tries++; - } - } - - PrintAndLogEx(INFO, "----------------------------- " _CYAN_("exit") " ----------------------------------\n"); - t1 = msclock() - t1; - PrintAndLogEx(SUCCESS, "\ntime in unlock " _YELLOW_("%.0f") " seconds\n", (float)t1 / 1000.0); - if (success) { - uint32_t bitflips = search_value ^ word14b; - PrintAndLogEx(INFO, "Old protection word => " _YELLOW_("%08X"), search_value); - char bitstring[9] = {0}; - for (int i=0; i < 8; i++) { - bitstring[i] = bitflips & (0xF << ((7-i) * 4)) ? 'x' : '.'; - } - // compute number of bits flipped - - PrintAndLogEx(INFO, "Bitflips: %2u events => %s", bitcount32(bitflips), bitstring); - PrintAndLogEx(INFO, "New protection word => " _CYAN_("%08X") "\n", word14b); - - - PrintAndLogEx(INFO, "Try " _YELLOW_("`lf em 4x05_dump`")); - } - - if (verbose) { - PrintAndLogEx(NORMAL, "Stats:"); - PrintAndLogEx(INFO, " idx | value | cnt | flipped bits"); - PrintAndLogEx(INFO, "-----+----------+-----+------"); - for (uint8_t i = 0; i < 64; i++) { - if (flipped[i].cnt == 0) - break; - - PrintAndLogEx(INFO, " %3u | %08X | %3u | %u", i, flipped[i].value, flipped[i].cnt, bitcount32(search_value ^ flipped[i].value)); - } - } - PrintAndLogEx(NORMAL, ""); - return exit_code; -} - static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help"}, {"----------", CmdHelp, AlwaysAvailable, "----------------------- " _CYAN_("EM 410x") " -----------------------"}, diff --git a/client/src/cmdlfem4x.h b/client/src/cmdlfem4x.h index 670517092..34801000e 100644 --- a/client/src/cmdlfem4x.h +++ b/client/src/cmdlfem4x.h @@ -16,10 +16,8 @@ int CmdLFEM4X(const char *Cmd); int demodEM410x(bool verbose); -bool EM4x05IsBlock0(uint32_t *word); -int EM4x05ReadWord_ext(uint8_t addr, uint32_t pwd, bool usePwd, uint32_t *word); - void printEM410x(uint32_t hi, uint64_t id); + int AskEm410xDecode(bool verbose, uint32_t *hi, uint64_t *lo); int AskEm410xDemod(int clk, int invert, int maxErr, size_t maxLen, bool amplify, uint32_t *hi, uint64_t *lo, bool verbose); diff --git a/client/src/cmdlfem4x05.c b/client/src/cmdlfem4x05.c new file mode 100644 index 000000000..f8f88b40a --- /dev/null +++ b/client/src/cmdlfem4x05.c @@ -0,0 +1,1474 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2010 iZsh +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// Low frequency EM4x commands +//----------------------------------------------------------------------------- + +#include "cmdlfem4x05.h" + +#include +#include +#include +#include +#include + +#include "util_posix.h" // msclock +#include "fileutils.h" +#include "cmdparser.h" // command_t +#include "comms.h" +#include "commonutil.h" +#include "common.h" +#include "util_posix.h" +#include "protocols.h" +#include "ui.h" +#include "proxgui.h" +#include "graph.h" +#include "cmddata.h" +#include "cmdlf.h" +#include "lfdemod.h" +#include "generator.h" +#include "cliparser.h" +#include "cmdhw.h" + +//////////////// 4205 / 4305 commands +static int usage_lf_em4x05_dump(void) { + PrintAndLogEx(NORMAL, "Dump EM4x05/EM4x69. Tag must be on antenna. "); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Usage: lf em 4x05_dump [h] [f ] "); + PrintAndLogEx(NORMAL, "Options:"); + PrintAndLogEx(NORMAL, " h - this help"); + PrintAndLogEx(NORMAL, " f - overide filename prefix (optional). Default is based on UID"); + PrintAndLogEx(NORMAL, " pwd - password (hex) (optional)"); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, " lf em 4x05_dump"); + PrintAndLogEx(NORMAL, " lf em 4x05_dump 11223344"); + PrintAndLogEx(NORMAL, " lf em 4x05_dump f card1 11223344"); + return PM3_SUCCESS; +} +static int usage_lf_em4x05_wipe(void) { + PrintAndLogEx(NORMAL, "Wipe EM4x05/EM4x69. Tag must be on antenna. "); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Usage: lf em 4x05_wipe [h] "); + PrintAndLogEx(NORMAL, "Options:"); + PrintAndLogEx(NORMAL, " h - this help"); + PrintAndLogEx(NORMAL, " c - chip type : 0 em4205"); + PrintAndLogEx(NORMAL, " 1 em4305 (default)"); + PrintAndLogEx(NORMAL, " pwd - password (hex) (optional)"); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, " lf em 4x05_wipe"); + PrintAndLogEx(NORMAL, " lf em 4x05_wipe 11223344"); + return PM3_SUCCESS; +} +static int usage_lf_em4x05_read(void) { + PrintAndLogEx(NORMAL, "Read EM4x05/EM4x69. Tag must be on antenna. "); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Usage: lf em 4x05_read [h]
"); + PrintAndLogEx(NORMAL, "Options:"); + PrintAndLogEx(NORMAL, " h - this help"); + PrintAndLogEx(NORMAL, " address - memory address to read. (0-15)"); + PrintAndLogEx(NORMAL, " pwd - password (hex) (optional)"); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, " lf em 4x05_read 1"); + PrintAndLogEx(NORMAL, " lf em 4x05_read 1 11223344"); + return PM3_SUCCESS; +} +static int usage_lf_em4x05_write(void) { + PrintAndLogEx(NORMAL, "Write EM4x05/4x69. Tag must be on antenna. "); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Usage: lf em 4x05_write [h]
"); + PrintAndLogEx(NORMAL, "Options:"); + PrintAndLogEx(NORMAL, " h - this help"); + PrintAndLogEx(NORMAL, " address - memory address to write to. (0-13, 99 for Protection Words)"); + PrintAndLogEx(NORMAL, " data - data to write (hex)"); + PrintAndLogEx(NORMAL, " pwd - password (hex) (optional)"); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, " lf em 4x05_write 1 deadc0de"); + PrintAndLogEx(NORMAL, " lf em 4x05_write 1 deadc0de 11223344"); + return PM3_SUCCESS; +} +static int usage_lf_em4x05_info(void) { + PrintAndLogEx(NORMAL, "Tag information EM4205/4305/4469//4569 tags. Tag must be on antenna."); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Usage: lf em 4x05_info [h] "); + PrintAndLogEx(NORMAL, "Options:"); + PrintAndLogEx(NORMAL, " h - this help"); + PrintAndLogEx(NORMAL, " pwd - password (hex) (optional)"); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, " lf em 4x05_info"); + PrintAndLogEx(NORMAL, " lf em 4x05_info deadc0de"); + return PM3_SUCCESS; +} + +// even parity COLUMN +static bool EM_ColParityTest(uint8_t *bs, size_t size, uint8_t rows, uint8_t cols, uint8_t pType) { + if (rows * cols > size) return false; + uint8_t colP = 0; + + for (uint8_t c = 0; c < cols - 1; c++) { + for (uint8_t r = 0; r < rows; r++) { + colP ^= bs[(r * cols) + c]; + } + if (colP != pType) return false; + colP = 0; + } + return true; +} + +#define EM_PREAMBLE_LEN 6 +// download samples from device and copy to Graphbuffer +static bool downloadSamplesEM(void) { + + // 8 bit preamble + 32 bit word response (max clock (128) * 40bits = 5120 samples) + uint8_t got[6000]; + if (!GetFromDevice(BIG_BUF, got, sizeof(got), 0, NULL, 0, NULL, 2500, false)) { + PrintAndLogEx(WARNING, "(downloadSamplesEM) command execution time out"); + return false; + } + + setGraphBuf(got, sizeof(got)); + // set signal properties low/high/mean/amplitude and is_noise detection + computeSignalProperties(got, sizeof(got)); + RepaintGraphWindow(); + if (getSignalProperties()->isnoise) { + PrintAndLogEx(DEBUG, "No tag found - signal looks like noise"); + return false; + } + return true; +} + +// em_demod +static int doPreambleSearch(size_t *startIdx) { + + // sanity check + if (DemodBufferLen < EM_PREAMBLE_LEN) { + PrintAndLogEx(DEBUG, "DEBUG: Error - EM4305 demodbuffer too small"); + return PM3_ESOFT; + } + + // set size to 9 to only test first 3 positions for the preamble + // do not set it too long else an error preamble followed by 010 could be seen as success. + size_t size = (9 > DemodBufferLen) ? DemodBufferLen : 9; + *startIdx = 0; + // skip first two 0 bits as they might have been missed in the demod + uint8_t preamble[EM_PREAMBLE_LEN] = {0, 0, 1, 0, 1, 0}; + + if (!preambleSearchEx(DemodBuffer, preamble, EM_PREAMBLE_LEN, &size, startIdx, true)) { + uint8_t errpreamble[EM_PREAMBLE_LEN] = {0, 0, 0, 0, 0, 1}; + if (!preambleSearchEx(DemodBuffer, errpreamble, EM_PREAMBLE_LEN, &size, startIdx, true)) { + PrintAndLogEx(DEBUG, "DEBUG: Error - EM4305 preamble not found :: %zu", *startIdx); + return PM3_ESOFT; + } + return PM3_EFAILED; // Error preamble found + } + return PM3_SUCCESS; +} + +static bool detectFSK(void) { + // detect fsk clock + if (GetFskClock("", false) == 0) { + PrintAndLogEx(DEBUG, "DEBUG: Error - EM: FSK clock failed"); + return false; + } + // demod + int ans = FSKrawDemod(0, 0, 0, 0, false); + if (ans != PM3_SUCCESS) { + PrintAndLogEx(DEBUG, "DEBUG: Error - EM: FSK Demod failed"); + return false; + } + return true; +} +// PSK clocks should be easy to detect ( but difficult to demod a non-repeating pattern... ) +static bool detectPSK(void) { + int ans = GetPskClock("", false); + if (ans <= 0) { + PrintAndLogEx(DEBUG, "DEBUG: Error - EM: PSK clock failed"); + return false; + } + //demod + //try psk1 -- 0 0 6 (six errors?!?) + ans = PSKDemod(0, 0, 6, false); + if (ans != PM3_SUCCESS) { + PrintAndLogEx(DEBUG, "DEBUG: Error - EM: PSK1 Demod failed"); + + //try psk1 inverted + ans = PSKDemod(0, 1, 6, false); + if (ans != PM3_SUCCESS) { + PrintAndLogEx(DEBUG, "DEBUG: Error - EM: PSK1 inverted Demod failed"); + return false; + } + } + // either PSK1 or PSK1 inverted is ok from here. + // lets check PSK2 later. + return true; +} +// try manchester - NOTE: ST only applies to T55x7 tags. +static bool detectASK_MAN(void) { + bool stcheck = false; + if (ASKDemod_ext(0, 0, 50, 0, false, false, false, 1, &stcheck) != PM3_SUCCESS) { + PrintAndLogEx(DEBUG, "DEBUG: Error - EM: ASK/Manchester Demod failed"); + return false; + } + return true; +} + +static bool detectASK_BI(void) { + int ans = ASKbiphaseDemod(0, 0, 1, 50, false); + if (ans != PM3_SUCCESS) { + PrintAndLogEx(DEBUG, "DEBUG: Error - EM: ASK/biphase normal demod failed"); + + ans = ASKbiphaseDemod(0, 1, 1, 50, false); + if (ans != PM3_SUCCESS) { + PrintAndLogEx(DEBUG, "DEBUG: Error - EM: ASK/biphase inverted demod failed"); + return false; + } + } + return true; +} +static bool detectNRZ(void) { + int ans = NRZrawDemod(0, 0, 1, false); + if (ans != PM3_SUCCESS) { + PrintAndLogEx(DEBUG, "DEBUG: Error - EM: NRZ normal demod failed"); + + ans = NRZrawDemod(0, 1, 1, false); + if (ans != PM3_SUCCESS) { + PrintAndLogEx(DEBUG, "DEBUG: Error - EM: NRZ inverted demod failed"); + return false; + } + } + + return true; +} + +// param: idx - start index in demoded data. +static int setDemodBufferEM(uint32_t *word, size_t idx) { + + //test for even parity bits. + uint8_t parity[45] = {0}; + memcpy(parity, DemodBuffer, 45); + if (!EM_ColParityTest(DemodBuffer + idx + EM_PREAMBLE_LEN, 45, 5, 9, 0)) { + PrintAndLogEx(DEBUG, "DEBUG: Error - End Parity check failed"); + return PM3_ESOFT; + } + + // test for even parity bits and remove them. (leave out the end row of parities so 36 bits) + if (!removeParity(DemodBuffer, idx + EM_PREAMBLE_LEN, 9, 0, 36)) { + PrintAndLogEx(DEBUG, "DEBUG: Error - EM, failed removing parity"); + return PM3_ESOFT; + } + setDemodBuff(DemodBuffer, 32, 0); + *word = bytebits_to_byteLSBF(DemodBuffer, 32); + return PM3_SUCCESS; +} + +// FSK, PSK, ASK/MANCHESTER, ASK/BIPHASE, ASK/DIPHASE, NRZ +// should cover 90% of known used configs +// the rest will need to be manually demoded for now... +static int demodEM4x05resp(uint32_t *word, bool onlyPreamble) { + size_t idx = 0; + *word = 0; + bool found_err = false; + int res = PM3_SUCCESS; + do { + if (detectASK_MAN()) { + res = doPreambleSearch(&idx); + if (res == PM3_SUCCESS) + break; + if (res == PM3_EFAILED) + // go on, maybe it's false positive and another modulation will work + found_err = true; + } + if (detectASK_BI()) { + res = doPreambleSearch(&idx); + if (res == PM3_SUCCESS) + break; + if (res == PM3_EFAILED) + found_err = true; + } + if (detectNRZ()) { + res = doPreambleSearch(&idx); + if (res == PM3_SUCCESS) + break; + if (res == PM3_EFAILED) + found_err = true; + } + if (detectFSK()) { + res = doPreambleSearch(&idx); + if (res == PM3_SUCCESS) + break; + if (res == PM3_EFAILED) + found_err = true; + } + if (detectPSK()) { + res = doPreambleSearch(&idx); + if (res == PM3_SUCCESS) + break; + if (res == PM3_EFAILED) + found_err = true; + + psk1TOpsk2(DemodBuffer, DemodBufferLen); + res = doPreambleSearch(&idx); + if (res == PM3_SUCCESS) + break; + if (res == PM3_EFAILED) + found_err = true; + } + if (found_err) + return PM3_EFAILED; + return PM3_ESOFT; + } while (0); + if (onlyPreamble) + return PM3_SUCCESS; + res = setDemodBufferEM(word, idx); + if (res == PM3_SUCCESS) + return res; + if (found_err) + return PM3_EFAILED; + return res; +} + +//////////////// 4205 / 4305 commands + +int EM4x05ReadWord_ext(uint8_t addr, uint32_t pwd, bool usePwd, uint32_t *word) { + + struct { + uint32_t password; + uint8_t address; + uint8_t usepwd; + } PACKED payload; + + payload.password = pwd; + payload.address = addr; + payload.usepwd = usePwd; + + clearCommandBuffer(); + SendCommandNG(CMD_LF_EM4X_READWORD, (uint8_t *)&payload, sizeof(payload)); + PacketResponseNG resp; + if (!WaitForResponseTimeout(CMD_LF_EM4X_READWORD, &resp, 10000)) { + PrintAndLogEx(WARNING, "(EM4x05ReadWord_ext) timeout while waiting for reply."); + return PM3_ETIMEOUT; + } + + if (downloadSamplesEM() == false) { + return PM3_ESOFT; + } + return demodEM4x05resp(word, false); +} + +int CmdEM4x05Demod(const char *Cmd) { +// uint8_t ctmp = tolower(param_getchar(Cmd, 0)); +// if (ctmp == 'h') return usage_lf_em4x05_demod(); + uint32_t dummy = 0; + return demodEM4x05resp(&dummy, false); +} + +int CmdEM4x05Dump(const char *Cmd) { + uint8_t addr = 0; + uint32_t pwd = 0; + bool usePwd = false; + bool needReadPwd = true; + uint8_t cmdp = 0; + uint8_t bytes[4] = {0}; + uint32_t data[16]; + char preferredName[FILE_PATH_SIZE] = {0}; + char optchk[10]; + + while (param_getchar(Cmd, cmdp) != 0x00) { + switch (tolower(param_getchar(Cmd, cmdp))) { + case 'h': + return usage_lf_em4x05_dump(); + break; + case 'f': // since f could match in password, lets confirm it is 1 character only for an option + param_getstr(Cmd, cmdp, optchk, sizeof(optchk)); + if (strlen(optchk) == 1) { // Have a single character f so filename no password + param_getstr(Cmd, cmdp + 1, preferredName, FILE_PATH_SIZE); + cmdp += 2; + break; + } // if not a single 'f' dont break and flow onto default as should be password + + default : // for backwards-compatibility options should be > 'f' else assume its the hex password` + // for now use default input of 1 as invalid (unlikely 1 will be a valid password...) + pwd = param_get32ex(Cmd, cmdp, 1, 16); + if (pwd != 1) + usePwd = true; + cmdp++; + }; + } + + int success = PM3_SUCCESS; + int status, status14, status15; + uint32_t lock_bits = 0x00; // no blocks locked + bool gotLockBits = false; + bool lockInPW2 = false; + uint32_t word = 0; + const char *info[] = {"Info/User", "UID", "Password", "User", "Config", "User", "User", "User", "User", "User", "User", "User", "User", "User", "Lock", "Lock"}; + + if (usePwd) { + // Test first if a password is required + status = EM4x05ReadWord_ext(14, pwd, false, &word); + if (status == PM3_SUCCESS) { + PrintAndLogEx(INFO, "Note that password doesn't seem to be needed"); + needReadPwd = false; + } + } + PrintAndLogEx(NORMAL, "Addr | data | ascii |lck| info"); + PrintAndLogEx(NORMAL, "-----+----------+-------+---+-----"); + + // To flag any blocks locked we need to read blocks 14 and 15 first + // dont swap endin until we get block lock flags. + status14 = EM4x05ReadWord_ext(14, pwd, usePwd, &word); + if (status14 == PM3_SUCCESS) { + if (!usePwd) + needReadPwd = false; + if ((word & 0x00008000) != 0x00) { + lock_bits = word; + gotLockBits = true; + } + data[14] = word; + } else { + success = PM3_ESOFT; // If any error ensure fail is set so not to save invalid data + } + status15 = EM4x05ReadWord_ext(15, pwd, usePwd, &word); + if (status15 == PM3_SUCCESS) { + if ((word & 0x00008000) != 0x00) { // assume block 15 is the current lock block + lock_bits = word; + gotLockBits = true; + lockInPW2 = true; + } + data[15] = word; + } else { + success = PM3_ESOFT; // If any error ensure fail is set so not to save invalid data + } + uint32_t lockbit; + // Now read blocks 0 - 13 as we have 14 and 15 + for (; addr < 14; addr++) { + lockbit = (lock_bits >> addr) & 1; + if (addr == 2) { + if (usePwd) { + if ((needReadPwd) && (success != PM3_ESOFT)) { + data[addr] = BSWAP_32(pwd); + num_to_bytes(pwd, 4, bytes); + PrintAndLogEx(NORMAL, " %02u | %08X | %s | %s | %s", addr, pwd, sprint_ascii(bytes, 4), gotLockBits ? (lockbit ? _RED_("x") : " ") : _YELLOW_("?"), info[addr]); + } else { + // The pwd is not needed for Login so we're not sure what's the actual content of that block + PrintAndLogEx(NORMAL, " %02u | | | | %-10s " _YELLOW_("write only"), addr, info[addr]); + } + } else { + data[addr] = 0x00; // Unknown password, but not used to set to zeros + PrintAndLogEx(NORMAL, " %02u | | | | %-10s " _YELLOW_("write only"), addr, info[addr]); + } + } else { + // success &= EM4x05ReadWord_ext(addr, pwd, usePwd, &word); + status = EM4x05ReadWord_ext(addr, pwd, usePwd, &word); // Get status for single read + if (status != PM3_SUCCESS) + success = PM3_ESOFT; // If any error ensure fail is set so not to save invalid data + data[addr] = BSWAP_32(word); + if (status == PM3_SUCCESS) { + num_to_bytes(word, 4, bytes); + PrintAndLogEx(NORMAL, " %02u | %08X | %s | %s | %s", addr, word, sprint_ascii(bytes, 4), gotLockBits ? (lockbit ? _RED_("x") : " ") : _YELLOW_("?"), info[addr]); + } else + PrintAndLogEx(NORMAL, " %02u | | | | %-10s %s", addr, info[addr], status == PM3_EFAILED ? _RED_("read denied") : _RED_("read failed")); + } + } + // Print blocks 14 and 15 + // Both lock bits are protected with bit idx 14 (special case) + addr = 14; + if (status14 == PM3_SUCCESS) { + lockbit = (lock_bits >> addr) & 1; + PrintAndLogEx(NORMAL, " %02u | %08X | %s | %s | %-10s %s", addr, data[addr], sprint_ascii(bytes, 4), gotLockBits ? (lockbit ? _RED_("x") : " ") : _YELLOW_("?"), info[addr], lockInPW2 ? "" : _GREEN_("active")); + } else { + PrintAndLogEx(NORMAL, " %02u | | | | %-10s %s", addr, info[addr], status14 == PM3_EFAILED ? _RED_("read denied") : _RED_("read failed")); + } + addr = 15; + if (status15 == PM3_SUCCESS) { + lockbit = (lock_bits >> 14) & 1; // beware lock bit of word15 is pr14 + PrintAndLogEx(NORMAL, " %02u | %08X | %s | %s | %-10s %s", addr, data[addr], sprint_ascii(bytes, 4), gotLockBits ? (lockbit ? _RED_("x") : " ") : _YELLOW_("?"), info[addr], lockInPW2 ? _GREEN_("active") : ""); + } else { + PrintAndLogEx(NORMAL, " %02u | | | | %-10s %s", addr, info[addr], status15 == PM3_EFAILED ? _RED_("read denied") : _RED_("read failed")); + } + // Update endian for files + data[14] = BSWAP_32(data[14]); + data[15] = BSWAP_32(data[15]); + + if (success == PM3_SUCCESS) { // all ok save dump to file + // saveFileEML will add .eml extension to filename + // saveFile (binary) passes in the .bin extension. + if (strcmp(preferredName, "") == 0) // Set default filename, if not set by user + sprintf(preferredName, "lf-4x05-%08X-dump", BSWAP_32(data[1])); + + saveFileEML(preferredName, (uint8_t *)data, 16 * sizeof(uint32_t), sizeof(uint32_t)); + saveFile(preferredName, ".bin", data, sizeof(data)); + } + + return success; +} + +int CmdEM4x05Read(const char *Cmd) { + uint8_t addr; + uint32_t pwd; + bool usePwd = false; + + uint8_t ctmp = tolower(param_getchar(Cmd, 0)); + if (strlen(Cmd) == 0 || ctmp == 'h') return usage_lf_em4x05_read(); + + addr = param_get8ex(Cmd, 0, 50, 10); + pwd = param_get32ex(Cmd, 1, 0xFFFFFFFF, 16); + + if (addr > 15) { + PrintAndLogEx(WARNING, "Address must be between 0 and 15"); + return PM3_ESOFT; + } + if (pwd == 0xFFFFFFFF) { + PrintAndLogEx(INFO, "Reading address %02u", addr); + } else { + usePwd = true; + PrintAndLogEx(INFO, "Reading address %02u using password %08X", addr, pwd); + } + + uint32_t word = 0; + int status = EM4x05ReadWord_ext(addr, pwd, usePwd, &word); + if (status == PM3_SUCCESS) + PrintAndLogEx(SUCCESS, "Address %02d | %08X - %s", addr, word, (addr > 13) ? "Lock" : ""); + else if (status == PM3_EFAILED) + PrintAndLogEx(ERR, "Tag denied Read operation"); + else + PrintAndLogEx(WARNING, "No answer from tag"); + return status; +} + +int CmdEM4x05Write(const char *Cmd) { + uint8_t ctmp = tolower(param_getchar(Cmd, 0)); + if (strlen(Cmd) == 0 || ctmp == 'h') return usage_lf_em4x05_write(); + + bool usePwd = false; + uint8_t addr; + uint32_t data, pwd; + + addr = param_get8ex(Cmd, 0, 50, 10); + data = param_get32ex(Cmd, 1, 0, 16); + pwd = param_get32ex(Cmd, 2, 0xFFFFFFFF, 16); + bool protectOperation = addr == 99; // will do better with cliparser... + + if ((addr > 13) && (!protectOperation)) { + PrintAndLogEx(WARNING, "Address must be between 0 and 13"); + return PM3_EINVARG; + } + if (pwd == 0xFFFFFFFF) { + if (protectOperation) + PrintAndLogEx(INFO, "Writing protection words data %08X", data); + else + PrintAndLogEx(INFO, "Writing address %d data %08X", addr, data); + } else { + usePwd = true; + if (protectOperation) + PrintAndLogEx(INFO, "Writing protection words data %08X using password %08X", data, pwd); + else + PrintAndLogEx(INFO, "Writing address %d data %08X using password %08X", addr, data, pwd); + } + + if (protectOperation) { // set Protect Words + struct { + uint32_t password; + uint32_t data; + uint8_t usepwd; + } PACKED payload; + + payload.password = pwd; + payload.data = data; + payload.usepwd = usePwd; + + clearCommandBuffer(); + SendCommandNG(CMD_LF_EM4X_PROTECTWORD, (uint8_t *)&payload, sizeof(payload)); + PacketResponseNG resp; + if (!WaitForResponseTimeout(CMD_LF_EM4X_PROTECTWORD, &resp, 2000)) { + PrintAndLogEx(ERR, "Error occurred, device did not respond during write operation."); + return PM3_ETIMEOUT; + } + } else { + struct { + uint32_t password; + uint32_t data; + uint8_t address; + uint8_t usepwd; + } PACKED payload; + + payload.password = pwd; + payload.data = data; + payload.address = addr; + payload.usepwd = usePwd; + + clearCommandBuffer(); + SendCommandNG(CMD_LF_EM4X_WRITEWORD, (uint8_t *)&payload, sizeof(payload)); + PacketResponseNG resp; + if (!WaitForResponseTimeout(CMD_LF_EM4X_WRITEWORD, &resp, 2000)) { + PrintAndLogEx(ERR, "Error occurred, device did not respond during write operation."); + return PM3_ETIMEOUT; + } + } + if (!downloadSamplesEM()) + return PM3_ENODATA; + + uint32_t dummy = 0; + int status = demodEM4x05resp(&dummy, true); + if (status == PM3_SUCCESS) + PrintAndLogEx(SUCCESS, "Success writing to tag"); + else if (status == PM3_EFAILED) + PrintAndLogEx(ERR, "Tag denied %s operation", protectOperation ? "Protect" : "Write"); + else + PrintAndLogEx(DEBUG, "No answer from tag"); + + PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf em 4x05_read`") " to verify"); + return status; +} + +int CmdEM4x05Wipe(const char *Cmd) { + uint8_t addr = 0; + uint32_t pwd = 0; + uint8_t cmdp = 0; + uint8_t chipType = 1; // em4305 + uint32_t chipInfo = 0x00040072; // Chip info/User Block normal 4305 Chip Type + uint32_t chipUID = 0x614739AE; // UID normally readonly, but just in case + uint32_t blockData = 0x00000000; // UserBlock/Password (set to 0x00000000 for a wiped card1 + uint32_t config = 0x0001805F; // Default config (no password) + int success = PM3_SUCCESS; + char cmdStr [100]; + char optchk[10]; + + while (param_getchar(Cmd, cmdp) != 0x00) { + // check if cmd is a 1 byte option + param_getstr(Cmd, cmdp, optchk, sizeof(optchk)); + if (strlen(optchk) == 1) { // Have a single character so option not part of password + switch (tolower(param_getchar(Cmd, cmdp))) { + case 'c': // chip type + if (param_getchar(Cmd, cmdp) != 0x00) + chipType = param_get8ex(Cmd, cmdp + 1, 0, 10); + cmdp += 2; + break; + case 'h': // return usage_lf_em4x05_wipe(); + default : // Unknown or 'h' send help + return usage_lf_em4x05_wipe(); + break; + }; + } else { // Not a single character so assume password + pwd = param_get32ex(Cmd, cmdp, 1, 16); + cmdp++; + } + } + + switch (chipType) { + case 0 : // em4205 + chipInfo = 0x00040070; + config = 0x0001805F; + break; + case 1 : // em4305 + chipInfo = 0x00040072; + config = 0x0001805F; + break; + default : // Type 0/Default : EM4305 + chipInfo = 0x00040072; + config = 0x0001805F; + } + + // block 0 : User Data or Chip Info + sprintf(cmdStr, "%d %08X %08X", 0, chipInfo, pwd); + CmdEM4x05Write(cmdStr); + // block 1 : UID - this should be read only for EM4205 and EM4305 not sure about others + sprintf(cmdStr, "%d %08X %08X", 1, chipUID, pwd); + CmdEM4x05Write(cmdStr); + // block 2 : password + sprintf(cmdStr, "%d %08X %08X", 2, blockData, pwd); + CmdEM4x05Write(cmdStr); + pwd = blockData; // Password should now have changed, so use new password + // block 3 : user data + sprintf(cmdStr, "%d %08X %08X", 3, blockData, pwd); + CmdEM4x05Write(cmdStr); + // block 4 : config + sprintf(cmdStr, "%d %08X %08X", 4, config, pwd); + CmdEM4x05Write(cmdStr); + + // Remainder of user/data blocks + for (addr = 5; addr < 14; addr++) {// Clear user data blocks + sprintf(cmdStr, "%d %08X %08X", addr, blockData, pwd); + CmdEM4x05Write(cmdStr); + } + + return success; +} + +static void printEM4x05config(uint32_t wordData) { + uint16_t datarate = (((wordData & 0x3F) + 1) * 2); + uint8_t encoder = ((wordData >> 6) & 0xF); + char enc[14]; + memset(enc, 0, sizeof(enc)); + + uint8_t PSKcf = (wordData >> 10) & 0x3; + char cf[10]; + memset(cf, 0, sizeof(cf)); + uint8_t delay = (wordData >> 12) & 0x3; + char cdelay[33]; + memset(cdelay, 0, sizeof(cdelay)); + uint8_t numblks = EM4x05_GET_NUM_BLOCKS(wordData); + uint8_t LWR = numblks + 5 - 1; //last word read + switch (encoder) { + case 0: + snprintf(enc, sizeof(enc), "NRZ"); + break; + case 1: + snprintf(enc, sizeof(enc), "Manchester"); + break; + case 2: + snprintf(enc, sizeof(enc), "Biphase"); + break; + case 3: + snprintf(enc, sizeof(enc), "Miller"); + break; + case 4: + snprintf(enc, sizeof(enc), "PSK1"); + break; + case 5: + snprintf(enc, sizeof(enc), "PSK2"); + break; + case 6: + snprintf(enc, sizeof(enc), "PSK3"); + break; + case 7: + snprintf(enc, sizeof(enc), "Unknown"); + break; + case 8: + snprintf(enc, sizeof(enc), "FSK1"); + break; + case 9: + snprintf(enc, sizeof(enc), "FSK2"); + break; + default: + snprintf(enc, sizeof(enc), "Unknown"); + break; + } + + switch (PSKcf) { + case 0: + snprintf(cf, sizeof(cf), "RF/2"); + break; + case 1: + snprintf(cf, sizeof(cf), "RF/8"); + break; + case 2: + snprintf(cf, sizeof(cf), "RF/4"); + break; + case 3: + snprintf(cf, sizeof(cf), "unknown"); + break; + } + + switch (delay) { + case 0: + snprintf(cdelay, sizeof(cdelay), "no delay"); + break; + case 1: + snprintf(cdelay, sizeof(cdelay), "BP/8 or 1/8th bit period delay"); + break; + case 2: + snprintf(cdelay, sizeof(cdelay), "BP/4 or 1/4th bit period delay"); + break; + case 3: + snprintf(cdelay, sizeof(cdelay), "no delay"); + break; + } + uint8_t readLogin = (wordData & EM4x05_READ_LOGIN_REQ) >> 18; + uint8_t readHKL = (wordData & EM4x05_READ_HK_LOGIN_REQ) >> 19; + uint8_t writeLogin = (wordData & EM4x05_WRITE_LOGIN_REQ) >> 20; + uint8_t writeHKL = (wordData & EM4x05_WRITE_HK_LOGIN_REQ) >> 21; + uint8_t raw = (wordData & EM4x05_READ_AFTER_WRITE) >> 22; + uint8_t disable = (wordData & EM4x05_DISABLE_ALLOWED) >> 23; + uint8_t rtf = (wordData & EM4x05_READER_TALK_FIRST) >> 24; + uint8_t pigeon = (wordData & (1 << 26)) >> 26; + + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "--- " _CYAN_("Config Information") " ---------------------------"); + PrintAndLogEx(INFO, "ConfigWord: %08X (Word 4)", wordData); + PrintAndLogEx(INFO, " Data Rate: %02u | "_YELLOW_("RF/%u"), wordData & 0x3F, datarate); + PrintAndLogEx(INFO, " Encoder: %u | " _YELLOW_("%s"), encoder, enc); + PrintAndLogEx(INFO, " PSK CF: %u | %s", PSKcf, cf); + PrintAndLogEx(INFO, " Delay: %u | %s", delay, cdelay); + PrintAndLogEx(INFO, " LastWordR: %02u | Address of last word for default read - meaning %u blocks are output", LWR, numblks); + PrintAndLogEx(INFO, " ReadLogin: %u | Read login is %s", readLogin, readLogin ? _YELLOW_("required") : _GREEN_("not required")); + PrintAndLogEx(INFO, " ReadHKL: %u | Read housekeeping words login is %s", readHKL, readHKL ? _YELLOW_("required") : _GREEN_("not required")); + PrintAndLogEx(INFO, "WriteLogin: %u | Write login is %s", writeLogin, writeLogin ? _YELLOW_("required") : _GREEN_("not required")); + PrintAndLogEx(INFO, " WriteHKL: %u | Write housekeeping words login is %s", writeHKL, writeHKL ? _YELLOW_("required") : _GREEN_("not Required")); + PrintAndLogEx(INFO, " R.A.W.: %u | Read after write is %s", raw, raw ? "on" : "off"); + PrintAndLogEx(INFO, " Disable: %u | Disable command is %s", disable, disable ? "accepted" : "not accepted"); + PrintAndLogEx(INFO, " R.T.F.: %u | Reader talk first is %s", rtf, rtf ? _YELLOW_("enabled") : "disabled"); + PrintAndLogEx(INFO, " Pigeon: %u | Pigeon mode is %s", pigeon, pigeon ? _YELLOW_("enabled") : "disabled"); +} + +static void printEM4x05info(uint32_t block0, uint32_t serial) { + + uint8_t chipType = (block0 >> 1) & 0xF; + uint8_t cap = (block0 >> 5) & 3; + uint16_t custCode = (block0 >> 9) & 0x2FF; + + PrintAndLogEx(INFO, " block0: %X", block0); + PrintAndLogEx(INFO, " chiptype: %X", chipType); + PrintAndLogEx(INFO, "capacitor: %X", cap); + PrintAndLogEx(INFO, " custcode: %X", custCode); + + /* bits + // 0, rfu + // 1,2,3,4 chip type + // 5,6 resonant cap + // 7,8, rfu + // 9 - 18 customer code + // 19, rfu + + 98765432109876543210 + 001000000000 + // 00100000000001111000 + xxx---- + // 1100 + // 011 + // 00100000000 + */ + + PrintAndLogEx(INFO, "--- " _CYAN_("Tag Information") " ---------------------------"); + + char ctstr[50]; + snprintf(ctstr, sizeof(ctstr), " Chip Type: %u | ", chipType); + switch (chipType) { + case 9: + snprintf(ctstr + strlen(ctstr), sizeof(ctstr) - strlen(ctstr), _YELLOW_("%s"), "EM4305"); + break; + case 4: + snprintf(ctstr + strlen(ctstr), sizeof(ctstr) - strlen(ctstr), _YELLOW_("%s"), "EM4469"); + break; + case 8: + snprintf(ctstr + strlen(ctstr), sizeof(ctstr) - strlen(ctstr), _YELLOW_("%s"), "EM4205"); + break; + //add more here when known + default: + snprintf(ctstr + strlen(ctstr), sizeof(ctstr) - strlen(ctstr), _YELLOW_("%s"), "Unknown"); + break; + } + PrintAndLogEx(SUCCESS, "%s", ctstr); + + switch (cap) { + case 3: + PrintAndLogEx(SUCCESS, " Cap Type: %u | 330pF", cap); + break; + case 2: + PrintAndLogEx(SUCCESS, " Cap Type: %u | %spF", cap, (chipType == 2) ? "75" : "210"); + break; + case 1: + PrintAndLogEx(SUCCESS, " Cap Type: %u | 250pF", cap); + break; + case 0: + PrintAndLogEx(SUCCESS, " Cap Type: %u | no resonant capacitor", cap); + break; + default: + PrintAndLogEx(SUCCESS, " Cap Type: %u | unknown", cap); + break; + } + + PrintAndLogEx(SUCCESS, " Cust Code: %03u | %s", custCode, (custCode == 0x200) ? "Default" : "Unknown"); + if (serial != 0) + PrintAndLogEx(SUCCESS, " Serial #: " _YELLOW_("%08X"), serial); +} + +static void printEM4x05ProtectionBits(uint32_t word, uint8_t addr) { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "--- " _CYAN_("Protection") " ---------------------------"); + PrintAndLogEx(INFO, "ProtectionWord: %08X (Word %i)", word, addr); + for (uint8_t i = 0; i < 15; i++) { + PrintAndLogEx(INFO, " Word: %02u | %s", i, ((1 << i) & word) ? _RED_("write Locked") : "unlocked"); + if (i == 14) + PrintAndLogEx(INFO, " Word: %02u | %s", i + 1, ((1 << i) & word) ? _RED_("write locked") : "unlocked"); + } +} + +//quick test for EM4x05/EM4x69 tag +bool EM4x05IsBlock0(uint32_t *word) { + return (EM4x05ReadWord_ext(0, 0, false, word) == PM3_SUCCESS); +} + +int CmdEM4x05Info(const char *Cmd) { +#define EM_SERIAL_BLOCK 1 +#define EM_CONFIG_BLOCK 4 +#define EM_PROT1_BLOCK 14 +#define EM_PROT2_BLOCK 15 + uint32_t pwd; + uint32_t word = 0, block0 = 0, serial = 0; + bool usePwd = false; + uint8_t ctmp = tolower(param_getchar(Cmd, 0)); + if (ctmp == 'h') return usage_lf_em4x05_info(); + + // for now use default input of 1 as invalid (unlikely 1 will be a valid password...) + pwd = param_get32ex(Cmd, 0, 0xFFFFFFFF, 16); + + if (pwd != 0xFFFFFFFF) + usePwd = true; + + // read word 0 (chip info) + // block 0 can be read even without a password. + if (EM4x05IsBlock0(&block0) == false) + return PM3_ESOFT; + + // read word 1 (serial #) doesn't need pwd + // continue if failed, .. non blocking fail. + EM4x05ReadWord_ext(EM_SERIAL_BLOCK, 0, false, &serial); + printEM4x05info(block0, serial); + + // read word 4 (config block) + // needs password if one is set + if (EM4x05ReadWord_ext(EM_CONFIG_BLOCK, pwd, usePwd, &word) != PM3_SUCCESS) + return PM3_ESOFT; + + printEM4x05config(word); + + // read word 14 and 15 to see which is being used for the protection bits + if (EM4x05ReadWord_ext(EM_PROT1_BLOCK, pwd, usePwd, &word) != PM3_SUCCESS) { + return PM3_ESOFT; + } + if (word & 0x8000) { + printEM4x05ProtectionBits(word, EM_PROT1_BLOCK); + return PM3_SUCCESS; + } else { // if status bit says this is not the used protection word + if (EM4x05ReadWord_ext(EM_PROT2_BLOCK, pwd, usePwd, &word) != PM3_SUCCESS) + return PM3_ESOFT; + if (word & 0x8000) { + printEM4x05ProtectionBits(word, EM_PROT2_BLOCK); + return PM3_SUCCESS; + } + } + //something went wrong + return PM3_ESOFT; +} + +static bool is_cancelled(void) { + if (kbd_enter_pressed()) { + PrintAndLogEx(WARNING, "\naborted via keyboard!\n"); + return true; + } + return false; +} +// load a default pwd file. +int CmdEM4x05Chk(const char *Cmd) { + + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf em 4x05_chk", + "This command uses a dictionary attack against EM4205/4305/4469/4569", + "lf em 4x05_chk\n" + "lf em 4x05_chk -e 0x00000022B8 -> remember to use 0x for hex\n" + "lf em 4x05_chk -f t55xx_default_pwds -> use T55xx default dictionary" + ); + + void *argtable[] = { + arg_param_begin, + arg_strx0("f", "file", "<*.dic>", "loads a default keys dictionary file <*.dic>"), + arg_u64_0("e", "em", "", "try the calculated password from some cloners based on EM4100 ID"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + int fnlen = 0; + char filename[FILE_PATH_SIZE] = {0}; + CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); + uint64_t card_id = arg_get_u64_def(ctx, 2, 0); + CLIParserFree(ctx); + + if (strlen(filename) == 0) { + snprintf(filename, sizeof(filename), "t55xx_default_pwds"); + } + PrintAndLogEx(NORMAL, ""); + + uint8_t addr = 4; + uint32_t word = 0; + bool found = false; + uint64_t t1 = msclock(); + + // White cloner password based on EM4100 ID + if ( card_id > 0 ) { + + uint32_t pwd = lf_t55xx_white_pwdgen(card_id & 0xFFFFFFFF); + PrintAndLogEx(INFO, "testing %08"PRIX32" generated ", pwd); + + int status = EM4x05ReadWord_ext(addr, pwd, true, &word); + if (status == PM3_SUCCESS) { + PrintAndLogEx(SUCCESS, "found valid password [ " _GREEN_("%08"PRIX32) " ]", pwd); + found = true; + } + } + + // Loop dictionary + uint8_t *keyBlock = NULL; + if (found == false) { + + PrintAndLogEx(INFO, "press " _YELLOW_("'enter'") " to cancel the command"); + + word = 0; + uint32_t keycount = 0; + + int res = loadFileDICTIONARY_safe(filename, (void **) &keyBlock, 4, &keycount); + if (res != PM3_SUCCESS || keycount == 0 || keyBlock == NULL) { + PrintAndLogEx(WARNING, "no keys found in file"); + if (keyBlock != NULL) + free(keyBlock); + + return PM3_ESOFT; + } + + for (uint32_t c = 0; c < keycount; ++c) { + + if (!session.pm3_present) { + PrintAndLogEx(WARNING, "device offline\n"); + free(keyBlock); + return PM3_ENODATA; + } + + if (is_cancelled()) { + free(keyBlock); + return PM3_EOPABORTED; + } + + uint32_t curr_password = bytes_to_num(keyBlock + 4 * c, 4); + + PrintAndLogEx(INFO, "testing %08"PRIX32, curr_password); + + int status = EM4x05ReadWord_ext(addr, curr_password, 1, &word); + if (status == PM3_SUCCESS) { + PrintAndLogEx(SUCCESS, "found valid password [ " _GREEN_("%08"PRIX32) " ]", curr_password); + found = true; + break; + } + } + } + + if (found == false) + PrintAndLogEx(WARNING, "check pwd failed"); + + free(keyBlock); + + t1 = msclock() - t1; + PrintAndLogEx(SUCCESS, "\ntime in check pwd " _YELLOW_("%.0f") " seconds\n", (float)t1 / 1000.0); + return PM3_SUCCESS; +} + +typedef struct { + uint16_t cnt; + uint32_t value; +} em4x05_unlock_item_t; + +static int unlock_write_protect(bool use_pwd, uint32_t pwd, uint32_t data, bool verbose) { + + struct { + uint32_t password; + uint32_t data; + uint8_t usepwd; + } PACKED payload; + + payload.password = pwd; + payload.data = data; + payload.usepwd = use_pwd; + + clearCommandBuffer(); + SendCommandNG(CMD_LF_EM4X_PROTECTWORD, (uint8_t *)&payload, sizeof(payload)); + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_LF_EM4X_PROTECTWORD, &resp, 2000) == false) { + PrintAndLogEx(ERR, "Error occurred, device did not respond during write operation."); + return PM3_ETIMEOUT; + } + + if (!downloadSamplesEM()) + return PM3_ENODATA; + + uint32_t dummy = 0; + int status = demodEM4x05resp(&dummy, true); + if (status == PM3_SUCCESS && verbose) + PrintAndLogEx(SUCCESS, "Success writing to tag"); + else if (status == PM3_EFAILED) + PrintAndLogEx(ERR, "Tag denied PROTECT operation"); + else + PrintAndLogEx(DEBUG, "No answer from tag"); + + return status; +} +static int unlock_reset(bool use_pwd, uint32_t pwd, uint32_t data, bool verbose) { + if (verbose) + PrintAndLogEx(INFO, "resetting the " _RED_("active") " lock block"); + + return unlock_write_protect(use_pwd, pwd, data, false); +} +static void unlock_add_item(em4x05_unlock_item_t *array, uint8_t len, uint32_t value) { + + uint8_t i = 0; + for (; i < len; i++) { + if ( array[i].value == value ) { + array[i].cnt++; + break; + } + if ( array[i].cnt == 0 ) { + array[i].cnt++; + array[i].value = value; + break; + } + } +} + +int CmdEM4x05Unlock(const char *Cmd) { + + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf em 4x05_unlock", + "execute tear off against EM4205/4305/4469/4569", + "lf em 4x05_unlock\n" + "lf em 4x05_unlock -s 4100 -e 4100 -> lock on and autotune at 4100us\n" + "lf em 4x05_unlock -n 10 -s 3000 -e 4400 -> scan delays 3000us -> 4400us" + ); + + void *argtable[] = { + arg_param_begin, + arg_int0("n", NULL, NULL, "steps to skip"), + arg_int0("s", "start", "", "start scan from delay (us)"), + arg_int0("e", "end", "", "end scan at delay (us)"), + arg_u64_0("p", "pwd", "", "password (0x00000000)"), + arg_lit0("v", "verbose", "verbose output"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + double n = (double)arg_get_int_def(ctx, 1, 0); + double start = (double)arg_get_int_def(ctx, 2, 2000); + double end = (double)arg_get_int_def(ctx, 3, 6000); + uint64_t inputpwd = arg_get_u64_def(ctx, 4, 0xFFFFFFFFFFFFFFFF); + bool verbose = arg_get_lit(ctx, 5); + CLIParserFree(ctx); + + if ( start > end ) { + PrintAndLogEx(FAILED, "start delay can\'t be larger than end delay %.0lf vs %.0lf", start, end); + return PM3_EINVARG; + } + + if (session.pm3_present == false) { + PrintAndLogEx(WARNING, "device offline\n"); + return PM3_ENODATA; + } + + bool use_pwd = false; + uint32_t pwd = 0; + if (inputpwd != 0xFFFFFFFFFFFFFFFF) { + use_pwd = true; + pwd = inputpwd & 0xFFFFFFFF; + } + + uint32_t search_value = 0; + uint32_t write_value = 0; + // + // inital phase + // + // read word 14 + uint32_t init_14 = 0; + int res = EM4x05ReadWord_ext(14, pwd, use_pwd, &init_14); + if (res != PM3_SUCCESS) { + PrintAndLogEx(FAILED, "failed to read word 14\n"); + return PM3_ENODATA; + } + + + // read 15 + uint32_t init_15 = 0; + res = EM4x05ReadWord_ext(15, pwd, use_pwd, &init_15); + if (res != PM3_SUCCESS) { + PrintAndLogEx(FAILED, "failed to read word 15\n"); + return PM3_ENODATA; + } + +#define ACTIVE_MASK 0x00008000 + if ((init_15 & ACTIVE_MASK) == ACTIVE_MASK) { + search_value = init_15; + } else { + search_value = init_14; + } + + if (search_value == ACTIVE_MASK) { + PrintAndLogEx(SUCCESS, "Tag already fully unlocked, nothing to do"); + return PM3_SUCCESS; + } + + bool my_auto = false; + if (n == 0) { + my_auto = true; + n = (end - start) / 2; + } + + // fix at one specific delay + if (start == end) { + n = 0; + } + + PrintAndLogEx(INFO, "--------------- " _CYAN_("EM4x05 tear-off : target PROTECT") " -----------------------\n"); + + PrintAndLogEx(INFO, "initial prot 14&15 [ " _GREEN_("%08X") ", " _GREEN_("%08X") " ]", init_14, init_15); + + if (use_pwd) { + PrintAndLogEx(INFO, " target password [ " _GREEN_("%08X") " ]", pwd); + } + if (my_auto) { + PrintAndLogEx(INFO, " automatic mode [ " _GREEN_("enabled") " ]"); + } + + PrintAndLogEx(INFO, " target stepping [ " _GREEN_("%.0lf") " ]", n); + PrintAndLogEx(INFO, "target delay range [ " _GREEN_("%.0lf") " ... " _GREEN_("%.0lf") " ]", start, end); + PrintAndLogEx(INFO, " search value [ " _GREEN_("%08X") " ]", search_value); + PrintAndLogEx(INFO, " write value [ " _GREEN_("%08X") " ]", write_value); + + PrintAndLogEx(INFO, "----------------------------------------------------------------------------\n"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "press " _YELLOW_("'enter'") " to cancel the command"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "--------------- " _CYAN_("start") " -----------------------\n"); + + int exit_code = PM3_SUCCESS; + uint32_t word14 = 0, word15 = 0; + uint32_t word14b = 0, word15b = 0; + uint32_t tries = 0; + uint32_t soon = 0; + uint32_t late = 0; + + em4x05_unlock_item_t flipped[64]; + + // + // main loop + // + bool success = false; + uint64_t t1 = msclock(); + while (start <= end) { + + if (my_auto && n < 1) { + PrintAndLogEx(INFO, "Reached n < 1 => " _YELLOW_("disabling automatic mode")); + end = start; + my_auto = false; + n = 0; + } + + if (my_auto == false) { + start += n; + } + + if (tries >= 5 && n == 0 && soon != late) { + + if (soon > late) { + PrintAndLogEx(INFO, "Tried %d times, soon:%i late:%i => " _CYAN_("adjust +1 us >> %.0lf us"), tries, soon, late, start); + start++; + end++; + } else { + PrintAndLogEx(INFO, "Tried %d times, soon:%i late:%i => " _CYAN_("adjust -1 us >> %.0lf us"), tries, soon, late, start); + start--; + end--; + } + tries = 0; + soon = 0; + late = 0; + } + + if (is_cancelled()) { + exit_code = PM3_EOPABORTED; + break; + } + + // set tear off trigger + clearCommandBuffer(); + tearoff_params_t params = { + .delay_us = start, + .on = true, + .off = false + }; + res = handle_tearoff(¶ms, verbose); + if ( res != PM3_SUCCESS ) { + PrintAndLogEx(WARNING, "failed to configure tear off"); + return PM3_ESOFT; + } + + // write + res = unlock_write_protect(use_pwd, pwd, write_value, verbose); + + // read after trigger + res = EM4x05ReadWord_ext(14, pwd, use_pwd, &word14); + if (res != PM3_SUCCESS) { + PrintAndLogEx(WARNING, "failed to read 14"); + return PM3_ESOFT; + } + + // read after trigger + res = EM4x05ReadWord_ext(15, pwd, use_pwd, &word15); + if (res != PM3_SUCCESS) { + PrintAndLogEx(WARNING, "failed to read 15"); + return PM3_ESOFT; + } + + if (verbose) + PrintAndLogEx(INFO, "ref:%08X 14:%08X 15:%08X ", search_value, word14, word15); + + if ( word14 == search_value && word15 == 0) { + PrintAndLogEx(INFO, "Status: Nothing happened => " _GREEN_("tearing too soon")); + + if (my_auto) { + start += n; + PrintAndLogEx(INFO, " => " _CYAN_("adjust +%.0lf us >> %.0lf us"), n, start); + n /= 2; + } else { + soon++; + } + } else { + + if (word15 == search_value) { + + if (word14 == 0) { + PrintAndLogEx(INFO, "Status: Protect succeeded => " _GREEN_("tearing too late")); + } else { + if ( word14 == search_value) { + PrintAndLogEx(INFO, "Status: 15 ok, 14 not yet erased => " _GREEN_("tearing too late")); + } else { + PrintAndLogEx(INFO, "Status: 15 ok, 14 partially erased => " _GREEN_("tearing too late")); + } + } + + unlock_reset(use_pwd, pwd, write_value, verbose); + + // read after reset + res = EM4x05ReadWord_ext(14, pwd, use_pwd, &word14b); + if (res != PM3_SUCCESS) { + PrintAndLogEx(WARNING, "failed to read 14"); + return PM3_ESOFT; + } + + if (word14b == 0) { + + unlock_reset(use_pwd, pwd, write_value, verbose); + + res = EM4x05ReadWord_ext(14, pwd, use_pwd, &word14b); + if (res != PM3_SUCCESS) { + PrintAndLogEx(WARNING, "failed to read 14"); + return PM3_ESOFT; + } + } + + if (word14b != search_value) { + + res = EM4x05ReadWord_ext(15, pwd, use_pwd, &word15b); + if (res == PM3_SUCCESS) { + PrintAndLogEx(INFO, "Status: new definitive value! => " _RED_("SUCCESS:") " 14: " _CYAN_("%08X") " 15: %08X", word14b, word15b); + success = true; + break; + } else { + PrintAndLogEx(WARNING, "failed to read 15"); + return PM3_ESOFT; + } + } + if (my_auto) { + end = start; + start -= n; + PrintAndLogEx(INFO, " => " _CYAN_("adjust -%.0lf us >> %.0lf us"), n, start); + n /= 2; + } else { + late++; + } + + } else { + + if (( word15 & ACTIVE_MASK) == ACTIVE_MASK) { + + PrintAndLogEx(INFO, "Status: 15 bitflipped and active => " _RED_("SUCCESS?: ") "14: %08X 15: " _CYAN_("%08X"), word14, word15); + PrintAndLogEx(INFO, "Committing results..."); + + unlock_reset(use_pwd, pwd, write_value, verbose); + + // read after reset + res = EM4x05ReadWord_ext(14, pwd, use_pwd, &word14b); + if ( res != PM3_SUCCESS ) { + PrintAndLogEx(WARNING, "failed to read 14"); + return PM3_ESOFT; + } + + res = EM4x05ReadWord_ext(15, pwd, use_pwd, &word15b); + if ( res != PM3_SUCCESS ) { + PrintAndLogEx(WARNING, "failed to read 15"); + return PM3_ESOFT; + } + + if (verbose) + PrintAndLogEx(INFO, "ref:%08x 14:%08X 15:%08X", search_value, word14b, word15b); + + if ((word14b & ACTIVE_MASK) == ACTIVE_MASK) { + + if (word14b == word15) { + PrintAndLogEx(INFO, "Status: confirmed => " _RED_("SUCCESS: ") "14: " _CYAN_("%08X") " 15: %08X", word14b, word15b); + + unlock_add_item(flipped, 64, word14b); + success = true; + break; + } + + if (word14b != search_value) { + PrintAndLogEx(INFO, "Status: new definitive value! => " _RED_("SUCCESS: ") "14: " _CYAN_("%08X") " 15: %08X", word14b, word15b); + + unlock_add_item(flipped, 64, word14b); + success = true; + break; + } + + PrintAndLogEx(INFO, "Status: failed to commit bitflip => " _RED_("FAIL: ") "14: %08X 15: %08X", word14b, word15b); + } + if (my_auto) { + n = 0; + end = start; + } else { + tries = 0; + soon = 0; + late = 0; + } + } else { + PrintAndLogEx(INFO, "Status: 15 bitflipped but inactive => " _YELLOW_("PROMISING: ") "14: %08X 15: " _CYAN_("%08X"), word14, word15); + + unlock_add_item(flipped, 64, word15); + + soon ++; + } + } + } + + if (my_auto == false) { + tries++; + } + } + + PrintAndLogEx(INFO, "----------------------------- " _CYAN_("exit") " ----------------------------------\n"); + t1 = msclock() - t1; + PrintAndLogEx(SUCCESS, "\ntime in unlock " _YELLOW_("%.0f") " seconds\n", (float)t1 / 1000.0); + if (success) { + uint32_t bitflips = search_value ^ word14b; + PrintAndLogEx(INFO, "Old protection word => " _YELLOW_("%08X"), search_value); + char bitstring[9] = {0}; + for (int i=0; i < 8; i++) { + bitstring[i] = bitflips & (0xF << ((7-i) * 4)) ? 'x' : '.'; + } + // compute number of bits flipped + + PrintAndLogEx(INFO, "Bitflips: %2u events => %s", bitcount32(bitflips), bitstring); + PrintAndLogEx(INFO, "New protection word => " _CYAN_("%08X") "\n", word14b); + + + PrintAndLogEx(INFO, "Try " _YELLOW_("`lf em 4x05_dump`")); + } + + if (verbose) { + PrintAndLogEx(NORMAL, "Stats:"); + PrintAndLogEx(INFO, " idx | value | cnt | flipped bits"); + PrintAndLogEx(INFO, "-----+----------+-----+------"); + for (uint8_t i = 0; i < 64; i++) { + if (flipped[i].cnt == 0) + break; + + PrintAndLogEx(INFO, " %3u | %08X | %3u | %u", i, flipped[i].value, flipped[i].cnt, bitcount32(search_value ^ flipped[i].value)); + } + } + PrintAndLogEx(NORMAL, ""); + return exit_code; +} diff --git a/client/src/cmdlfem4x05.h b/client/src/cmdlfem4x05.h new file mode 100644 index 000000000..0c41f431f --- /dev/null +++ b/client/src/cmdlfem4x05.h @@ -0,0 +1,31 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2010 iZsh +// 2016, 2017 marshmellow, iceman +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// Low frequency EM4x05 commands +//----------------------------------------------------------------------------- + +#ifndef CMDLFEM4X05_H__ +#define CMDLFEM4X05_H__ + +#include "common.h" + +int CmdLFEM4X05(const char *Cmd); + +bool EM4x05IsBlock0(uint32_t *word); +int EM4x05ReadWord_ext(uint8_t addr, uint32_t pwd, bool usePwd, uint32_t *word); + + +int CmdEM4x05Demod(const char *Cmd); +int CmdEM4x05Dump(const char *Cmd); +int CmdEM4x05Read(const char *Cmd); +int CmdEM4x05Write(const char *Cmd); +int CmdEM4x05Wipe(const char *Cmd); +int CmdEM4x05Info(const char *Cmd); +int CmdEM4x05Chk(const char *Cmd); +int CmdEM4x05Unlock(const char *Cmd); + +#endif diff --git a/client/src/scripting.c b/client/src/scripting.c index e603ce359..8d990fe1b 100644 --- a/client/src/scripting.c +++ b/client/src/scripting.c @@ -36,7 +36,7 @@ #include "fileutils.h" // searchfile #include "cmdlf.h" // lf_config #include "generator.h" -#include "cmdlfem4x.h" // read 4305 +#include "cmdlfem4x05.h" // read 4305 #include "cmdlfem4x50.h" // read 4350 #include "em4x50.h" // 4x50 structs