diff --git a/armsrc/appmain.c b/armsrc/appmain.c index 26f1513da..5a465c2bd 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -1037,12 +1037,8 @@ static void PacketReceived(PacketCommandNG *packet) { em4x50_wipe((em4x50_data_t *)packet->data.asBytes); break; } - case CMD_LF_EM4X50_SIM: { - em4x50_sim((em4x50_data_t *)packet->data.asBytes); - break; - } - case CMD_LF_EM4X50_TEST: { - em4x50_test((em4x50_data_t *)packet->data.asBytes); + case CMD_LF_EM4X50_BRUTE: { + em4x50_brute((em4x50_data_t *)packet->data.asBytes); break; } #endif diff --git a/armsrc/em4x50.c b/armsrc/em4x50.c index 267a28310..ebf381a69 100644 --- a/armsrc/em4x50.c +++ b/armsrc/em4x50.c @@ -1009,9 +1009,11 @@ void em4x50_write(em4x50_data_t *etd) { void em4x50_write_password(em4x50_data_t *etd) { - // sinmple change of password + // simple change of password bool bsuccess = false; + uint8_t rpwd[4] = {0x0, 0x0, 0x0, 0x0}; + uint8_t rnewpwd[4] = {0x0, 0x0, 0x0, 0x0}; init_tag(); em4x50_setup_read(); @@ -1019,9 +1021,20 @@ void em4x50_write_password(em4x50_data_t *etd) { // set gHigh and gLow if (get_signalproperties() && find_em4x50_tag()) { + // lsb -> msb + rpwd[0] = reflect8(etd->password[3]); + rpwd[1] = reflect8(etd->password[2]); + rpwd[2] = reflect8(etd->password[1]); + rpwd[3] = reflect8(etd->password[0]); + + rnewpwd[0] = reflect8(etd->new_password[3]); + rnewpwd[1] = reflect8(etd->new_password[2]); + rnewpwd[2] = reflect8(etd->new_password[1]); + rnewpwd[3] = reflect8(etd->new_password[0]); + // login and change password - if (login(etd->password)) { - bsuccess = write_password(etd->password, etd->new_password); + if (login(rpwd)) { + bsuccess = write_password(rpwd, rnewpwd); } } @@ -1055,7 +1068,7 @@ void em4x50_wipe(em4x50_data_t *etd) { // to verify result reset EM4x50 if (reset()) { - // login not necessary because protectd word has been set to 0 + // login not necessary because protected word has been set to 0 // -> no read protected words // -> selective read can be called immediately if (selective_read(addresses)) { @@ -1087,167 +1100,6 @@ void em4x50_wipe(em4x50_data_t *etd) { reply_ng(CMD_ACK, bsuccess, (uint8_t *)tag.sectors, 238); } -static bool em4x50_sim_send_bit(uint8_t bit) { - - uint16_t check = 0; - - for (int t = 0; t < EM4X50_T_TAG_FULL_PERIOD; t++) { - - // wait until SSC_CLK goes HIGH - // used as a simple detection of a reader field? - while (!(AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_CLK)) { - WDT_HIT(); - if (check == 1000) { - if (BUTTON_PRESS()) - return false; - check = 0; - } - ++check; - } - - if (bit) - OPEN_COIL(); - else - SHORT_COIL(); - - check = 0; - - //wait until SSC_CLK goes LOW - while (AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_CLK) { - WDT_HIT(); - if (check == 1000) { - if (BUTTON_PRESS()) - return false; - check = 0; - } - ++check; - } - - if (t == EM4X50_T_TAG_HALF_PERIOD) - bit ^= 1; - - } - - return true; -} - -static bool em4x50_sim_send_byte(uint8_t byte) { - - // send byte - for (int i = 0; i < 8; i++) - if (!em4x50_sim_send_bit((byte >> (7 - i)) & 1)) - return false; - - return true; - -} - -static bool em4x50_sim_send_byte_with_parity(uint8_t byte) { - - uint8_t parity = 0x0; - - // send byte with parity (even) - for (int i = 0; i < 8; i++) - parity ^= (byte >> i) & 1; - - if (!em4x50_sim_send_byte(byte)) - return false;; - - if (!em4x50_sim_send_bit(parity)) - return false; - - return true; -} - -bool em4x50_sim_send_word(uint32_t word) { - - uint8_t cparity = 0x00; - - // 4 bytes each with even row parity bit - for (int i = 0; i < 4; i++) - if (!em4x50_sim_send_byte_with_parity((word >> ((3 - i) * 8)) & 0xFF)) - return false; - - // column parity - for (int i = 0; i < 8; i++) { - cparity <<= 1; - for (int j = 0; j < 4; j++) { - cparity ^= (((word >> ((3 - j) * 8)) & 0xFF) >> (7 - i)) & 1; - } - } - if (!em4x50_sim_send_byte(cparity)) - return false; - - // stop bit - if (!em4x50_sim_send_bit(0)) - return false; - - return true; -} - -bool em4x50_sim_send_listen_window(void) { - - //int i = 0; - uint16_t check = 0; - //uint8_t test[100] = {0}; - - for (int t = 0; t < 5 * EM4X50_T_TAG_FULL_PERIOD; t++) { - - // wait until SSC_CLK goes HIGH - while (!(AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_CLK)) { - WDT_HIT(); - if (check == 1000) { - if (BUTTON_PRESS()) - return false; - check = 0; - } - ++check; - } - - if (t >= 4 * EM4X50_T_TAG_FULL_PERIOD) { - SHORT_COIL(); - } else if (t >= 3 * EM4X50_T_TAG_FULL_PERIOD) { - OPEN_COIL(); - } else if (t >= EM4X50_T_TAG_FULL_PERIOD) { - SHORT_COIL(); - } else if (t >= EM4X50_T_TAG_HALF_PERIOD) { - OPEN_COIL(); - } else { - SHORT_COIL(); - } - - check = 0; - - //wait until SSC_CLK goes LOW - while (AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_CLK) { - WDT_HIT(); - if (check == 1000) { - if (BUTTON_PRESS()) - return false; - check = 0; - } - ++check; - } - } - - return true; -} - -void em4x50_sim(em4x50_data_t *etd) { - - bool bsuccess = false; - - lf_finalize(); - reply_ng(CMD_ACK, bsuccess, (uint8_t *)tag.sectors, 238); -} - -void em4x50_test(em4x50_data_t *etd) { - - bool bsuccess = true; - - reply_ng(CMD_ACK, bsuccess, (uint8_t *)tag.sectors, 238); -} - int em4x50_standalone_read(uint64_t *words) { int now = 0; @@ -1276,3 +1128,59 @@ int em4x50_standalone_read(uint64_t *words) { return now; } + +void em4x50_brute(em4x50_data_t *etd) { + + // searching for password in given range + + bool bsuccess = false; + int cnt = 0; + uint8_t bytes[4] ={0x0, 0x0, 0x0, 0x0}; + uint32_t pwd = 0x0, rpwd = 0x0; + + init_tag(); + em4x50_setup_read(); + + // set gHigh and gLow + if (get_signalproperties() && find_em4x50_tag()) { + + for (pwd = etd->start_password; pwd <= etd->stop_password; pwd++) { + + // lsb -> msb + rpwd = reflect32(pwd); + + for (int i = 0; i < 4; i++) + bytes[i] = (rpwd >> ((3 - i) * 8)) & 0xFF; + + if (login(bytes)) { + bsuccess = true; + break; + } + + // print password every 500 iterations + if ((++cnt % 500) == 0) { + + // print header + if (cnt == 500) { + Dbprintf(""); + Dbprintf("|---------+------------+------------|"); + Dbprintf("| no. | pwd (msb) | pwd (lsb) |"); + Dbprintf("|---------+------------+------------|"); + } + + // print data + Dbprintf("|%8i | 0x%08x | 0x%08x |", cnt, rpwd, pwd); + } + + if (BUTTON_PRESS()) + break; + } + + // print footer + if (cnt >= 500) + Dbprintf("|---------+------------+------------|"); + } + + lf_finalize(); + reply_ng(CMD_ACK, bsuccess, (uint8_t *)(&pwd), 32); +} diff --git a/armsrc/em4x50.h b/armsrc/em4x50.h index 7e4c00fba..d1f897b8a 100644 --- a/armsrc/em4x50.h +++ b/armsrc/em4x50.h @@ -26,7 +26,6 @@ void em4x50_write(em4x50_data_t *etd); void em4x50_write_password(em4x50_data_t *etd); void em4x50_read(em4x50_data_t *etd); void em4x50_wipe(em4x50_data_t *etd); -void em4x50_sim(em4x50_data_t *etd); -void em4x50_test(em4x50_data_t *etd); +void em4x50_brute(em4x50_data_t *etd); #endif /* EM4X50_H */ diff --git a/client/src/cmdlfem4x.c b/client/src/cmdlfem4x.c index 1d0fe2f6c..b09f2f102 100644 --- a/client/src/cmdlfem4x.c +++ b/client/src/cmdlfem4x.c @@ -1398,8 +1398,7 @@ static command_t CommandTable[] = { {"4x50_write_password", CmdEM4x50WritePassword, IfPm3EM4x50, "change passwword of EM4x50 tag"}, {"4x50_read", CmdEM4x50Read, IfPm3EM4x50, "read word data from EM4x50"}, {"4x50_wipe", CmdEM4x50Wipe, IfPm3EM4x50, "wipe data from EM4x50"}, - {"4x50_sim", CmdEM4x50Sim, IfPm3EM4x50, "simulate EM4x50 tag"}, - {"4x50_test", CmdEM4x50Test, IfPm3EM4x50, "test functionality for EM4x50"}, + {"4x50_brute", CmdEM4x50Brute, IfPm3EM4x50, "guess password of EM4x50"}, {NULL, NULL, NULL, NULL} }; diff --git a/client/src/cmdlfem4x50.c b/client/src/cmdlfem4x50.c index ecd4f9334..15ac9fb4b 100644 --- a/client/src/cmdlfem4x50.c +++ b/client/src/cmdlfem4x50.c @@ -37,8 +37,8 @@ static int usage_lf_em4x50_write(void) { PrintAndLogEx(NORMAL, "Options:"); PrintAndLogEx(NORMAL, " h - this help"); PrintAndLogEx(NORMAL, " a - memory address to write to (dec)"); - PrintAndLogEx(NORMAL, " w - word to write (hex)"); - PrintAndLogEx(NORMAL, " p - password (hex) (optional)"); + PrintAndLogEx(NORMAL, " w - word to write (hex, lsb notation)"); + PrintAndLogEx(NORMAL, " p - password (hex, lsb notation) (optional)"); PrintAndLogEx(NORMAL, "Examples:"); PrintAndLogEx(NORMAL, _YELLOW_(" lf em 4x50_write a 3 w deadc0de")); PrintAndLogEx(NORMAL, ""); @@ -50,8 +50,8 @@ static int usage_lf_em4x50_write_password(void) { PrintAndLogEx(NORMAL, "Usage: lf em 4x50_write_password [h] [p ] [n ]"); PrintAndLogEx(NORMAL, "Options:"); PrintAndLogEx(NORMAL, " h - this help"); - PrintAndLogEx(NORMAL, " p - password (hex)"); - PrintAndLogEx(NORMAL, " n - new password (hex)"); + PrintAndLogEx(NORMAL, " p - password (hex, lsb notation)"); + PrintAndLogEx(NORMAL, " n - new password (hex, lsb notation)"); PrintAndLogEx(NORMAL, "Examples:"); PrintAndLogEx(NORMAL, _YELLOW_(" lf em 4x50_write_password p 11223344 n 01020304")); PrintAndLogEx(NORMAL, ""); @@ -122,6 +122,19 @@ static int usage_lf_em4x50_test(void) { PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; } +static int usage_lf_em4x50_brute(void) { + PrintAndLogEx(NORMAL, "Guess password of EM4x50 tag. Tag must be on antenna. "); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Usage: lf em 4x50_brute [h] f l "); + PrintAndLogEx(NORMAL, "Options:"); + PrintAndLogEx(NORMAL, " h - this help"); + PrintAndLogEx(NORMAL, " f - start password (hex, lsb notation)"); + PrintAndLogEx(NORMAL, " l - stop password (hex, lsb notation)"); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, _YELLOW_(" lf em 4x50_brute f 11200000 l 11300000")); + PrintAndLogEx(NORMAL, ""); + return PM3_SUCCESS; +} static void prepare_result(const uint8_t *byte, int fwr, int lwr, em4x50_word_t *words) { @@ -771,11 +784,11 @@ int CmdEM4x50Wipe(const char *Cmd) { return PM3_SUCCESS; } -int CmdEM4x50Sim(const char *Cmd) { +int CmdEM4x50Brute(const char *Cmd) { - // fills EM4x50 tag with zeros including password - - bool errors = false, bword = false; + bool startpwd = false, stoppwd = false, errors = false; + const int speed = 27; // 27 passwords/second (empirical value) + int no_iter = 0, dur_h = 0, dur_m = 0, dur_s = 0; uint8_t cmdp = 0; em4x50_data_t etd; PacketResponseNG resp; @@ -784,118 +797,47 @@ int CmdEM4x50Sim(const char *Cmd) { switch (tolower(param_getchar(Cmd, cmdp))) { case 'h': - return usage_lf_em4x50_sim(); - - case 'w': { - if (param_gethex(Cmd, cmdp + 1, etd.word, 8)) { - PrintAndLogEx(FAILED, "\n word has to be 8 hex symbols\n"); - return PM3_EINVARG; - } - bword = true; + return usage_lf_em4x50_brute(); + case 'f': + etd.start_password = param_get32ex(Cmd, cmdp + 1, 0, 16); + startpwd = true; cmdp += 2; break; - } - - case 'f': { - if (param_gethex(Cmd, cmdp + 1, etd.word, 8)) { - PrintAndLogEx(FAILED, "\n word has to be 8 hex symbols\n"); - return PM3_EINVARG; - } - bword = true; + case 'l': + etd.stop_password = param_get32ex(Cmd, cmdp + 1, 0, 16); + stoppwd = true; cmdp += 2; break; - } - default: - PrintAndLogEx(WARNING, "\nUnknown parameter '%c'\n", param_getchar(Cmd, cmdp)); + PrintAndLogEx(WARNING, "\n Unknown parameter '%c'\n", param_getchar(Cmd, cmdp)); errors = true; break; } } + + if (errors || !startpwd || !stoppwd) + return usage_lf_em4x50_brute(); - if (errors || !bword) - return usage_lf_em4x50_sim(); + // print some information + no_iter = etd.stop_password - etd.start_password + 1; + dur_s = no_iter / speed; + dur_h = dur_s / 3600; + dur_m = (dur_s - dur_h * 3600) / 60; + dur_s -= dur_h * 3600 + dur_m * 60; + PrintAndLogEx(NORMAL, "\ntrying %i passwords in range [0x%08x, 0x%08x]", + no_iter, etd.start_password, etd.stop_password); + PrintAndLogEx(NORMAL, "estimated duration: %ih%im%is\n", dur_h, dur_m, dur_s); + // start clearCommandBuffer(); - SendCommandNG(CMD_LF_EM4X50_SIM, (uint8_t *)&etd, sizeof(etd)); - - if (!WaitForResponse(CMD_ACK, &resp)) { - PrintAndLogEx(WARNING, "\ntimeout while waiting for reply.\n"); - return PM3_ETIMEOUT; - } + SendCommandNG(CMD_LF_EM4X50_BRUTE, (uint8_t *)&etd, sizeof(etd)); + WaitForResponse(CMD_ACK, &resp); // print response - bool isOK = resp.status; - if (isOK) { - PrintAndLogEx(SUCCESS, "\nsimulation data " _GREEN_("ok") "\n"); - } else { - PrintAndLogEx(FAILED, "\nsimulating data " _RED_("failed") "\n"); - return PM3_ESOFT; - } - - return PM3_SUCCESS; -} - -int CmdEM4x50Test(const char *Cmd) { - - // fills EM4x50 tag with zeros including password - - bool errors = false; - uint8_t cmdp = 0; - em4x50_data_t etd; - PacketResponseNG resp; - - etd.carrier = 2; - - while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { - - switch (tolower(param_getchar(Cmd, cmdp))) { - case 'h': - return usage_lf_em4x50_test(); - - case 'c': - param_getdec(Cmd, cmdp + 1, &etd.carrier); - if (etd.carrier != 0 && etd.carrier != 1) { - PrintAndLogEx(FAILED, "\ncarrier has to be either 0 or 1\n"); - return PM3_EINVARG; - } - cmdp += 2; - break; - - case 'b': - if (param_gethex(Cmd, cmdp + 1, &etd.byte, 2)) { - PrintAndLogEx(FAILED, "\nbyte has to be 2 hex symbols\n"); - return PM3_EINVARG; - } - cmdp += 2; - break; - - default: - PrintAndLogEx(WARNING, "\nUnknown parameter '%c'\n", param_getchar(Cmd, cmdp)); - errors = true; - break; - } - } - - if (errors) - return usage_lf_em4x50_test(); - - clearCommandBuffer(); - SendCommandNG(CMD_LF_EM4X50_TEST, (uint8_t *)&etd, sizeof(etd)); - - if (!WaitForResponse(CMD_ACK, &resp)) { - PrintAndLogEx(WARNING, "\ntimeout while waiting for reply.\n"); - return PM3_ETIMEOUT; - } - - // print response - bool isOK = resp.status; - if (isOK) { - PrintAndLogEx(SUCCESS, "\ntest " _GREEN_("ok") "\n"); - } else { - PrintAndLogEx(FAILED, "\ntest " _RED_("failed") "\n"); - return PM3_ESOFT; - } + if ((bool)resp.status) + PrintAndLogEx(NORMAL, "\npassword " _GREEN_("found") ": 0x%08x\n", resp.data.asDwords[0]); + else + PrintAndLogEx(NORMAL, "\npassword: " _RED_("not found") "\n"); return PM3_SUCCESS; } diff --git a/client/src/cmdlfem4x50.h b/client/src/cmdlfem4x50.h index 8a66fd7b8..9a86adfed 100644 --- a/client/src/cmdlfem4x50.h +++ b/client/src/cmdlfem4x50.h @@ -24,7 +24,6 @@ int CmdEM4x50WritePassword(const char *Cmd); int CmdEM4x50Read(const char *Cmd); int CmdEM4x50Dump(const char *Cmd); int CmdEM4x50Wipe(const char *Cmd); -int CmdEM4x50Sim(const char *Cmd); -int CmdEM4x50Test(const char *Cmd); +int CmdEM4x50Brute(const char *Cmd); #endif diff --git a/common/commonutil.c b/common/commonutil.c index 9be805f31..7da41e807 100644 --- a/common/commonutil.c +++ b/common/commonutil.c @@ -97,6 +97,22 @@ uint16_t reflect16(uint16_t b) { return v; } +uint32_t reflect32(uint32_t b) { + // https://graphics.stanford.edu/~seander/bithacks.html#BitReverseTable + uint32_t v = b; // 32-bit word to reverse bit order + // swap odd and even bits + v = ((v >> 1) & 0x55555555) | ((v & 0x55555555) << 1); + // swap consecutive pairs + v = ((v >> 2) & 0x33333333) | ((v & 0x33333333) << 2); + // swap nibbles ... + v = ((v >> 4) & 0x0F0F0F0F) | ((v & 0x0F0F0F0F) << 4); + // swap bytes + v = ((v >> 8) & 0x00FF00FF) | ((v & 0x00FF00FF) << 8); + // swap 2-byte long pairs + v = ( v >> 16 ) | ( v << 16); + return v; +} + void num_to_bytes(uint64_t n, size_t len, uint8_t *dest) { while (len--) { dest[len] = (uint8_t) n; @@ -153,4 +169,4 @@ uint32_t rotl(uint32_t a, uint8_t n) { uint32_t rotr(uint32_t a, uint8_t n) { n &= 31; return (a >> n) | (a << (32 - n)); -} \ No newline at end of file +} diff --git a/common/commonutil.h b/common/commonutil.h index 6bf330e7c..4ba65d171 100644 --- a/common/commonutil.h +++ b/common/commonutil.h @@ -47,6 +47,7 @@ void FormatVersionInformation(char *dst, int len, const char *prefix, void *vers uint32_t reflect(uint32_t v, int b); // used in crc.c ... uint8_t reflect8(uint8_t b); // dedicated 8bit reversal uint16_t reflect16(uint16_t b); // dedicated 16bit reversal +uint32_t reflect32(uint32_t b); // dedicated 32bit reversal void num_to_bytes(uint64_t n, size_t len, uint8_t *dest); uint64_t bytes_to_num(uint8_t *src, size_t len); diff --git a/include/em4x50.h b/include/em4x50.h index ea759ca97..771c360ee 100644 --- a/include/em4x50.h +++ b/include/em4x50.h @@ -51,6 +51,8 @@ typedef struct { uint8_t addresses[4]; uint8_t address; uint8_t word[4]; + uint32_t start_password; + uint32_t stop_password; } em4x50_data_t; typedef struct { diff --git a/include/pm3_cmd.h b/include/pm3_cmd.h index fa9150ef3..69a75a1ad 100644 --- a/include/pm3_cmd.h +++ b/include/pm3_cmd.h @@ -493,8 +493,7 @@ typedef struct { #define CMD_LF_EM4X50_WRITE_PASSWORD 0x0242 #define CMD_LF_EM4X50_READ 0x0243 #define CMD_LF_EM4X50_WIPE 0x0244 -#define CMD_LF_EM4X50_SIM 0x0245 -#define CMD_LF_EM4X50_TEST 0x0246 +#define CMD_LF_EM4X50_BRUTE 0x0245 // Sampling configuration for LF reader/sniffer #define CMD_LF_SAMPLING_SET_CONFIG 0x021D #define CMD_LF_FSK_SIMULATE 0x021E