diff --git a/armsrc/em4x50.c b/armsrc/em4x50.c index 841bfe973..2d3a24f89 100644 --- a/armsrc/em4x50.c +++ b/armsrc/em4x50.c @@ -35,11 +35,11 @@ #define EM4X50_T_TAG_TWA 64 #define EM4X50_T_WAITING_FOR_DBLLIW 1550 #define EM4X50_T_WAITING_FOR_SNGLLIW 140 // this value seems to be - // critical; if it's too - // low (e.g. < 120) some - // cards are no longer - // readable although - // they're ok + // critical; + // if it's too low + // (e.g. < 120) some cards + // are no longer readable + // although they're ok #define EM4X50_TAG_TOLERANCE 8 #define EM4X50_TAG_WORD 45 @@ -53,7 +53,9 @@ int gHigh = 190; int gLow = 60; +//============================================================================== // auxiliary functions +//============================================================================== static void wait_timer(uint32_t period) { @@ -63,6 +65,58 @@ static void wait_timer(uint32_t period) { while (AT91C_BASE_TC0->TC_CV < period); } +static bool extract_parities(uint64_t word, uint32_t *data) { + + // extract and check parities + // return result of parity check and extracted plain data + + uint8_t row_parities = 0x0, col_parities = 0x0; + uint8_t row_parities_calculated = 0x0, col_parities_calculated = 0x0; + + *data = 0x0; + + // extract plain data (32 bits) from raw word (45 bits) + for (int i = 0; i < 4; i++) { + *data <<= 8; + *data |= (word >> ((4 - i) * 9 + 1)) & 0xFF; + } + + // extract row parities (4 bits + stop bit) from raw word (45 bits) + for (int i = 0; i < 5; i++) { + row_parities <<= 1; + row_parities |= (word >> ((4 - i) * 9)) & 0x1; + } + + // extract col_parities (8 bits, no stop bit) from raw word (45 bits) + col_parities = (word >> 1) & 0xFF; + + // check extracted parities against extracted data + + // calculate row parities from data + for (int i = 0; i < 4; i++) { + row_parities_calculated <<= 1; + for (int j = 0; j < 8; j++) { + row_parities_calculated ^= (*data >> ((3 - i) * 8 + (7 - j))) & 0x1; + } + } + + // add stop bit (always zero) + row_parities_calculated <<= 1; + + // calculate column parities from data + for (int i = 0; i < 8; i++) { + col_parities_calculated <<= 1; + for (int j = 0; j < 4; j++) { + col_parities_calculated ^= (*data >> ((3 - j) * 8 + (7 - i))) & 0x1; + } + } + + if ((row_parities == row_parities_calculated) && (col_parities == col_parities_calculated)) + return true; + + return false; +} + static void em4x50_setup_read(void) { FpgaDownloadAndGo(FPGA_BITSTREAM_LF); @@ -120,7 +174,9 @@ static void em4x50_setup_sim(void) { AT91C_BASE_PIOA->PIO_ODR = GPIO_SSC_CLK; } +//============================================================================== // functions for "reader" use case +//============================================================================== static bool get_signalproperties(void) { @@ -402,6 +458,170 @@ static int find_double_listen_window(bool bcommand) { return false; } +static bool find_em4x50_tag(void) { + + // function is used to check wether a tag on the proxmark is an + // EM4x50 tag or not -> speed up "lf search" process + return find_single_listen_window(); +} + +static int request_receive_mode(void) { + + // To issue a command we have to find a listen window first. + // Because identification and synchronization at the same time is not + // possible when using pulse lengths a double listen window is used. + return find_double_listen_window(true); +} + +static bool check_ack(bool bliw) { + + // returns true if signal structue corresponds to ACK, anything else is + // counted as NAK (-> false) + // Only relevant for pasword writing function: + // If is true then within the single listen window right after the + // ack signal a RM request has to be sent. + + AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG; + while (AT91C_BASE_TC0->TC_CV < T0 * 4 * EM4X50_T_TAG_FULL_PERIOD) { + + if (check_pulse_length(get_pulse_length(), 2 * EM4X50_T_TAG_FULL_PERIOD)) { + + // The received signal is either ACK or NAK. + + if (check_pulse_length(get_pulse_length(), 2 * EM4X50_T_TAG_FULL_PERIOD)) { + + // Now the signal must be ACK. + + if (!bliw) { + + return true; + + } else { + + // send RM request after ack signal + + // wait for 2 bits (remaining "bit" of ACK signal + first + // "bit" of listen window) + wait_timer(T0 * 2 * EM4X50_T_TAG_FULL_PERIOD); + + // check for listen window (if first bit cannot be interpreted + // as a valid bit it must belong to a listen window) + if (invalid_bit()) { + + // send RM for request mode + em4x50_reader_send_bit(0); + em4x50_reader_send_bit(0); + + return true; + } + } + } else { + + // It's NAK -> stop searching + break; + } + } + } + + return false; +} + +static int get_word_from_bitstream(uint32_t *data) { + + // decodes one word by evaluating pulse lengths and previous bit; + // word must have 45 bits in total: + // 32 data bits + 4 row parity bits + 8 column parity bits + 1 stop bit + + bool bitchange = false; + int cnt = 0; + uint32_t pl = 0; + uint64_t word = 0x0; + + *data = 0x0; + + // initial bit value depends on last pulse length of listen window + pl = get_pulse_length(); + if (check_pulse_length(pl, 3 * EM4X50_T_TAG_HALF_PERIOD)) { + + // pulse length = 1.5 + word = 0x1; + + } else if (check_pulse_length(pl, 2 * EM4X50_T_TAG_FULL_PERIOD)) { + + // pulse length = 2 + bitchange = true; + + } else { + + // pulse length = 2.5 + word = 0x1; + cnt++; + } + + // identify remaining bits based on pulse lengths + // between two listen windows only pulse lengths of 1, 1.5 and 2 are possible + while (BUTTON_PRESS() == false) { + + cnt++; + word <<= 1; + + pl = get_pulse_length(); + + if (check_pulse_length(pl, EM4X50_T_TAG_FULL_PERIOD)) { + + // pulse length = 1 -> keep former bit value + word |= (word >> 1) & 0x1; + + } else if (check_pulse_length(pl, 3 * EM4X50_T_TAG_HALF_PERIOD)) { + + // pulse length = 1.5 -> decision on bit change + + if (bitchange) { + + // if number of pulse lengths with 1.5 periods is even -> add bit + word |= (word >> 1) & 0x1; + word <<= 1; + + // pulse length of 1.5 changes bit value + word |= ((word >> 1) & 0x1) ^ 0x1; + cnt++; + + // next time add only one bit + bitchange = false; + + } else { + + word |= ((word >> 1) & 0x1) ^ 0x1; + + // next time two bits have to be added + bitchange = true; + } + + } else if (check_pulse_length(pl, 2 * EM4X50_T_TAG_FULL_PERIOD)) { + + // pulse length of 2 means: adding 2 bits "01" + cnt++; + + word <<= 1; + word |= 0x1; + + } else if (check_pulse_length(pl, 3 * EM4X50_T_TAG_FULL_PERIOD)) { + + // pulse length of 3 indicates listen window -> clear last + // bit (= 0) and return (without parities) + word >>= 2; + return (extract_parities(word, data)) ? --cnt : 0; + + } + } + + return BUTTON_SINGLE_CLICK; +} + +//============================================================================== +// functions for "simulating" use case +//============================================================================== + static bool em4x50_sim_send_bit(uint8_t bit) { uint16_t check = 0; @@ -548,221 +768,8 @@ bool em4x50_sim_send_listen_window(void) { return true; } - -static bool find_em4x50_tag(void) { - - // function is used to check wether a tag on the proxmark is an - // EM4x50 tag or not -> speed up "lf search" process - return find_single_listen_window(); -} - -static int request_receive_mode(void) { - - // To issue a command we have to find a listen window first. - // Because identification and synchronization at the same time is not - // possible when using pulse lengths a double listen window is used. - return find_double_listen_window(true); -} - -static bool check_ack(bool bliw) { - - // returns true if signal structue corresponds to ACK, anything else is - // counted as NAK (-> false) - // Only relevant for pasword writing function: - // If is true then within the single listen window right after the - // ack signal a RM request has to be sent. - - AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG; - while (AT91C_BASE_TC0->TC_CV < T0 * 4 * EM4X50_T_TAG_FULL_PERIOD) { - - if (check_pulse_length(get_pulse_length(), 2 * EM4X50_T_TAG_FULL_PERIOD)) { - - // The received signal is either ACK or NAK. - - if (check_pulse_length(get_pulse_length(), 2 * EM4X50_T_TAG_FULL_PERIOD)) { - - // Now the signal must be ACK. - - if (!bliw) { - - return true; - - } else { - - // send RM request after ack signal - - // wait for 2 bits (remaining "bit" of ACK signal + first - // "bit" of listen window) - wait_timer(T0 * 2 * EM4X50_T_TAG_FULL_PERIOD); - - // check for listen window (if first bit cannot be interpreted - // as a valid bit it must belong to a listen window) - if (invalid_bit()) { - - // send RM for request mode - em4x50_reader_send_bit(0); - em4x50_reader_send_bit(0); - - return true; - } - } - } else { - - // It's NAK -> stop searching - break; - } - } - } - - return false; -} - -static bool extract_parities(uint64_t word, uint32_t *data) { - - // extract and check parities - // return result of parity check and extracted plain data - - uint8_t row_parities = 0x0, col_parities = 0x0; - uint8_t row_parities_calculated = 0x0, col_parities_calculated = 0x0; - - *data = 0x0; - - // extract plain data (32 bits) from raw word (45 bits) - for (int i = 0; i < 4; i++) { - *data <<= 8; - *data |= (word >> ((4 - i) * 9 + 1)) & 0xFF; - } - - // extract row parities (4 bits + stop bit) from raw word (45 bits) - for (int i = 0; i < 5; i++) { - row_parities <<= 1; - row_parities |= (word >> ((4 - i) * 9)) & 0x1; - } - - // extract col_parities (8 bits, no stop bit) from raw word (45 bits) - col_parities = (word >> 1) & 0xFF; - - // check extracted parities against extracted data - - // calculate row parities from data - for (int i = 0; i < 4; i++) { - row_parities_calculated <<= 1; - for (int j = 0; j < 8; j++) { - row_parities_calculated ^= (*data >> ((3 - i) * 8 + (7 - j))) & 0x1; - } - } - - // add stop bit (always zero) - row_parities_calculated <<= 1; - - // calculate column parities from data - for (int i = 0; i < 8; i++) { - col_parities_calculated <<= 1; - for (int j = 0; j < 4; j++) { - col_parities_calculated ^= (*data >> ((3 - j) * 8 + (7 - i))) & 0x1; - } - } - - if ((row_parities == row_parities_calculated) && (col_parities == col_parities_calculated)) - return true; - - return false; -} - -static int get_word_from_bitstream(uint32_t *data) { - - // decodes one word by evaluating pulse lengths and previous bit; - // word must have 45 bits in total: - // 32 data bits + 4 row parity bits + 8 column parity bits + 1 stop bit - - bool bitchange = false; - int cnt = 0; - uint32_t pl = 0; - uint64_t word = 0x0; - - *data = 0x0; - - // initial bit value depends on last pulse length of listen window - pl = get_pulse_length(); - if (check_pulse_length(pl, 3 * EM4X50_T_TAG_HALF_PERIOD)) { - - // pulse length = 1.5 - word = 0x1; - - } else if (check_pulse_length(pl, 2 * EM4X50_T_TAG_FULL_PERIOD)) { - - // pulse length = 2 - bitchange = true; - - } else { - - // pulse length = 2.5 - word = 0x1; - cnt++; - } - - // identify remaining bits based on pulse lengths - // between two listen windows only pulse lengths of 1, 1.5 and 2 are possible - while (BUTTON_PRESS() == false) { - - cnt++; - word <<= 1; - - pl = get_pulse_length(); - - if (check_pulse_length(pl, EM4X50_T_TAG_FULL_PERIOD)) { - - // pulse length = 1 -> keep former bit value - word |= (word >> 1) & 0x1; - - } else if (check_pulse_length(pl, 3 * EM4X50_T_TAG_HALF_PERIOD)) { - - // pulse length = 1.5 -> decision on bit change - - if (bitchange) { - - // if number of pulse lengths with 1.5 periods is even -> add bit - word |= (word >> 1) & 0x1; - word <<= 1; - - // pulse length of 1.5 changes bit value - word |= ((word >> 1) & 0x1) ^ 0x1; - cnt++; - - // next time add only one bit - bitchange = false; - - } else { - - word |= ((word >> 1) & 0x1) ^ 0x1; - - // next time two bits have to be added - bitchange = true; - } - - } else if (check_pulse_length(pl, 2 * EM4X50_T_TAG_FULL_PERIOD)) { - - // pulse length of 2 means: adding 2 bits "01" - cnt++; - - word <<= 1; - word |= 0x1; - - } else if (check_pulse_length(pl, 3 * EM4X50_T_TAG_FULL_PERIOD)) { - - // pulse length of 3 indicates listen window -> clear last - // bit (= 0) and return (without parities) - word >>= 2; - return (extract_parities(word, data)) ? --cnt : 0; - - } - } - - return BUTTON_SINGLE_CLICK; -} - //============================================================================== -// login function +// login functions //============================================================================== static bool login(uint32_t password) { @@ -792,8 +799,145 @@ static bool login(uint32_t password) { return PM3_EFAILED; } +static bool brute(uint32_t start, uint32_t stop, uint32_t *pwd) { + + // searching for password in given range + + bool pwd_found = false; + int cnt = 0; + + for (*pwd = start; *pwd <= stop; (*pwd)++) { + + if (login(*pwd) == PM3_SUCCESS) { + pwd_found = true; + break; + } + + // print password every 500 iterations + if ((++cnt % 500) == 0) { + + // print header + if (cnt == 500) { + Dbprintf("|---------+------------+------------|"); + Dbprintf("| no. | pwd (msb) | pwd (lsb) |"); + Dbprintf("|---------+------------+------------|"); + } + + // print data + Dbprintf("|%8i | 0x%08x | 0x%08x |", cnt, reflect32(*pwd), *pwd); + } + + if (BUTTON_PRESS()) + break; + + } + + // print footer + if (cnt >= 500) + Dbprintf("|---------+------------+------------|"); + + return pwd_found; +} + +void em4x50_login(uint32_t *password) { + + // login into EM4x50 + + uint8_t status = PM3_EFAILED; + + em4x50_setup_read(); + + // set gHigh and gLow + if (get_signalproperties() && find_em4x50_tag()) + status = login(*password); + + lf_finalize(); + reply_ng(CMD_LF_EM4X50_LOGIN, status, 0, 0); +} + +void em4x50_brute(em4x50_data_t *etd) { + + // envoke password search + + bool bsuccess = false; + uint32_t pwd = 0x0; + + em4x50_setup_read(); + + if (get_signalproperties() && find_em4x50_tag()) + bsuccess = brute(etd->password1, etd->password2, &pwd); + + lf_finalize(); + reply_ng(CMD_LF_EM4X50_BRUTE, bsuccess, (uint8_t *)(&pwd), 32); +} + +void em4x50_chk(uint32_t *offset) { + + // check passwords from dictionary content in flash memory + + int status = PM3_EFAILED; + uint8_t counter[2] = {0x00, 0x00}; + uint16_t isok = 0; + uint16_t pwd_count = 0; + uint16_t pwd_size_available = 0; + uint32_t pwd = 0x0; + uint8_t *pwds = BigBuf_get_EM_addr(); + + //----------------------------------------------------------------------------- + // Note: we call FpgaDownloadAndGo(FPGA_BITSTREAM_LF) here although FPGA is not + // involved in dealing with emulator memory. But if it is called later, it will + // destroy the Emulator Memory. + //----------------------------------------------------------------------------- + FpgaDownloadAndGo(FPGA_BITSTREAM_LF); + + BigBuf_Clear_EM(); + + // initialize passwords and get number of passwords + if (Flash_ReadData(*offset, counter, sizeof(counter)) != sizeof(counter)) + goto OUT; + + pwd_count = (uint16_t)(counter[1] << 8 | counter[0]); + if (pwd_count == 0) + goto OUT; + + pwd_size_available = 4 * pwd_count; + + isok = Flash_ReadData(*offset + 2, pwds, pwd_size_available); + if (isok != pwd_size_available) + goto OUT; + + em4x50_setup_read(); + + // set gHigh and gLow + if (get_signalproperties() && find_em4x50_tag()) { + + // try to login with current password + for (int i = 0; i < pwd_count; i++) { + + // manual interruption + if (BUTTON_PRESS()) { + status = BUTTON_SINGLE_CLICK; + break; + } + + // get next password + pwd = 0x0; + for (int j = 0; j < 4; j++) + pwd |= (*(pwds + 4 * i + j)) << ((3 - j) * 8); + + if ((status = login(pwd)) == PM3_SUCCESS) + break; + } + } + +OUT: + + lf_finalize(); + reply_ng(CMD_LF_EM4X50_CHK, status, (uint8_t *)&pwd, 32); +} + //============================================================================== -// reset function +// reset functions //============================================================================== static int reset(void) { @@ -816,6 +960,21 @@ static int reset(void) { return PM3_EFAILED; } +void em4x50_reset(void) { + + // reset EM4x50 + + uint8_t status = PM3_EFAILED; + + em4x50_setup_read(); + + // set gHigh and gLow + if (get_signalproperties() && find_em4x50_tag()) + status = reset(); + + lf_finalize(); + reply_ng(CMD_LF_EM4X50_RESET, status, 0, 0); +} //============================================================================== // read functions @@ -880,39 +1039,12 @@ static int selective_read(uint32_t addresses, uint32_t *words) { return status; } -void em4x50_info(em4x50_data_t *etd) { - - // collects as much information as possible via selective read mode - - bool bsuccess = false, blogin = false; - uint8_t status = 0; - uint32_t addresses = 0x00002100; // read from fwr = 0 to lwr = 33 (0x21) - uint32_t words[EM4X50_NO_WORDS] = {0x0}; - - em4x50_setup_read(); - - // set gHigh and gLow - if (get_signalproperties() && find_em4x50_tag()) { - - // login with given password - if (etd->pwd_given) - blogin = (login(etd->password1) == PM3_SUCCESS); - - bsuccess = (selective_read(addresses, words) == PM3_SUCCESS); - } - - status = (bsuccess << 1) + blogin; - - lf_finalize(); - reply_ng(CMD_LF_EM4X50_INFO, status, (uint8_t *)words, 136); -} - void em4x50_read(em4x50_data_t *etd) { // reads by using "selective read mode" -> bidirectional communication - bool bsuccess = false, blogin = false; - uint8_t status = 0; + bool blogin = true; + int status = PM3_EFAILED; uint32_t words[EM4X50_NO_WORDS] = {0x0}; em4x50_setup_read(); @@ -925,16 +1057,95 @@ void em4x50_read(em4x50_data_t *etd) { blogin = (login(etd->password1) == PM3_SUCCESS); // only one word has to be read -> first word read = last word read - bsuccess = (selective_read(etd->addresses, words) == PM3_SUCCESS); + if (blogin) + status = selective_read(etd->addresses, words); } - status = (bsuccess << 1) + blogin; - LOW(GPIO_SSC_DOUT); lf_finalize(); reply_ng(CMD_LF_EM4X50_READ, status, (uint8_t *)words, 136); } +void em4x50_stdread(void) { + + // reads data that tag transmits "voluntarily" -> standard read mode + + int now = 0; + uint32_t words[EM4X50_NO_WORDS] = {0x0}; + + em4x50_setup_read(); + + // set gHigh and gLow + if (get_signalproperties() && find_em4x50_tag()) + standard_read(&now, words); + + LOW(GPIO_SSC_DOUT); + lf_finalize(); + reply_ng(CMD_LF_EM4X50_STDREAD, now, (uint8_t *)words, 4 * now); +} + +void em4x50_info(em4x50_data_t *etd) { + + // collects as much information as possible via selective read mode + + bool blogin = true; + int status = PM3_EFAILED; + uint32_t addresses = 0x00002100; // read from fwr = 0 to lwr = 33 (0x21) + uint32_t words[EM4X50_NO_WORDS] = {0x0}; + + em4x50_setup_read(); + + // set gHigh and gLow + if (get_signalproperties() && find_em4x50_tag()) { + + // login with given password + if (etd->pwd_given) + blogin = (login(etd->password1) == PM3_SUCCESS); + + if (blogin) + status = selective_read(addresses, words); + } + + lf_finalize(); + reply_ng(CMD_LF_EM4X50_INFO, status, (uint8_t *)words, 136); +} + +void em4x50_watch() { + + // read continuously and display standard reads of tag + + int now = 0; + uint32_t words[EM4X50_NO_WORDS] = {0x0}; + + em4x50_setup_read(); + + while (BUTTON_PRESS() == false) { + + WDT_HIT(); + memset(words, 0, sizeof(words)); + now = 0; + + if (get_signalproperties() && find_em4x50_tag()) { + + if (standard_read(&now, words) == BUTTON_SINGLE_CLICK) + break; + + if (now > 0) { + + Dbprintf(""); + for (int i = 0; i < now; i++) + Dbprintf("EM4x50 tag data: " + _GREEN_("%08x") " (msb) - " _GREEN_("%08x") " (lsb)", + words[i], reflect32(words[i])); + } + } + } + + LOW(GPIO_SSC_DOUT); + lf_finalize(); + reply_ng(CMD_LF_EM4X50_WATCH, 1, 0, 0); +} + //============================================================================== // write functions //============================================================================== @@ -1028,9 +1239,9 @@ void em4x50_write(em4x50_data_t *etd) { // write operation process for EM4x50 tag, // single word is written to given address, verified by selective read operation + // wrong password -> return with PM3_EFAILED - bool bsuccess = false, blogin = false; - uint8_t status = 0; + int status = PM3_EFAILED; uint32_t words[EM4X50_NO_WORDS] = {0x0}; em4x50_setup_read(); @@ -1039,36 +1250,45 @@ void em4x50_write(em4x50_data_t *etd) { if (get_signalproperties() && find_em4x50_tag()) { // if password is given try to login first + status = PM3_SUCCESS; if (etd->pwd_given) - blogin = (login(etd->password1) == PM3_SUCCESS); + status = login(etd->password1); - // write word to given address - int res = write(etd->word, etd->addresses); - if (res == PM3_ETEAROFF) { - lf_finalize(); - return; - } + if (status == PM3_SUCCESS) { - if (res == PM3_SUCCESS) { + // write word to given address + status = write(etd->word, etd->addresses); + if (status == PM3_ETEAROFF) { + lf_finalize(); + return; + } - // to verify result reset EM4x50 - if (reset() == PM3_SUCCESS) { + if (status == PM3_SUCCESS) { - // if password is given login - if (etd->pwd_given) - blogin &= (login(etd->password1) == PM3_SUCCESS); + // to verify result reset EM4x50 + status = reset(); + if (status == PM3_SUCCESS) { - // call a selective read - if (selective_read(etd->addresses, words) == PM3_SUCCESS) { + // if password is given renew login after reset + if (etd->pwd_given) + status = login(etd->password1); + + if (status == PM3_SUCCESS) { - // compare with given word - bsuccess = (words[etd->addresses & 0xFF] == reflect32(etd->word)); + // call a selective read + status = selective_read(etd->addresses, words); + if (status == PM3_SUCCESS) { + + // compare result with given word + if (words[etd->addresses & 0xFF] != reflect32(etd->word)) + status = PM3_EFAILED; + } + } } } } } - status = (bsuccess << 1) + blogin; lf_finalize(); reply_ng(CMD_LF_EM4X50_WRITE, status, (uint8_t *)words, 136); } @@ -1077,7 +1297,7 @@ void em4x50_writepwd(em4x50_data_t *etd) { // simple change of password - int res = PM3_EFAILED; + int status = PM3_EFAILED; em4x50_setup_read(); @@ -1087,8 +1307,8 @@ void em4x50_writepwd(em4x50_data_t *etd) { // login and change password if (login(etd->password1) == PM3_SUCCESS) { - res = write_password(etd->password1, etd->password2); - if (res == PM3_ETEAROFF) { + status = write_password(etd->password1, etd->password2); + if (status == PM3_ETEAROFF) { lf_finalize(); return; } @@ -1096,177 +1316,16 @@ void em4x50_writepwd(em4x50_data_t *etd) { } lf_finalize(); - reply_ng(CMD_LF_EM4X50_WRITEPWD, res, 0, 0); -} - -void em4x50_reset(void) { - - // reset EM4x50 - - uint8_t status = PM3_EFAILED; - - em4x50_setup_read(); - - // set gHigh and gLow - if (get_signalproperties() && find_em4x50_tag()) - status = reset(); - - lf_finalize(); - reply_ng(CMD_LF_EM4X50_RESET, status, 0, 0); -} - -void em4x50_login(uint32_t *password) { - - // login into EM4x50 - - uint8_t status = PM3_EFAILED; - - em4x50_setup_read(); - - // set gHigh and gLow - if (get_signalproperties() && find_em4x50_tag()) - status = login(*password); - - lf_finalize(); - reply_ng(CMD_LF_EM4X50_LOGIN, status, 0, 0); -} - -static bool brute(uint32_t start, uint32_t stop, uint32_t *pwd) { - - // searching for password in given range - - bool pwd_found = false; - int cnt = 0; - - for (*pwd = start; *pwd <= stop; (*pwd)++) { - - if (login(*pwd) == PM3_SUCCESS) { - pwd_found = true; - break; - } - - // print password every 500 iterations - if ((++cnt % 500) == 0) { - - // print header - if (cnt == 500) { - Dbprintf("|---------+------------+------------|"); - Dbprintf("| no. | pwd (msb) | pwd (lsb) |"); - Dbprintf("|---------+------------+------------|"); - } - - // print data - Dbprintf("|%8i | 0x%08x | 0x%08x |", cnt, reflect32(*pwd), *pwd); - } - - if (BUTTON_PRESS()) - break; - - } - - // print footer - if (cnt >= 500) - Dbprintf("|---------+------------+------------|"); - - return pwd_found; -} - -void em4x50_brute(em4x50_data_t *etd) { - - // envoke password search - - bool bsuccess = false; - uint32_t pwd = 0x0; - - em4x50_setup_read(); - - if (get_signalproperties() && find_em4x50_tag()) - bsuccess = brute(etd->password1, etd->password2, &pwd); - - lf_finalize(); - reply_ng(CMD_LF_EM4X50_BRUTE, bsuccess, (uint8_t *)(&pwd), 32); -} - -void em4x50_watch() { - - // read continuously and display standard reads of tag - - int now = 0; - uint32_t words[EM4X50_NO_WORDS] = {0x0}; - - em4x50_setup_read(); - - while (BUTTON_PRESS() == false) { - - WDT_HIT(); - memset(words, 0, sizeof(words)); - now = 0; - - if (get_signalproperties() && find_em4x50_tag()) { - - if (standard_read(&now, words) == BUTTON_SINGLE_CLICK) - break; - - if (now > 0) { - - Dbprintf(""); - for (int i = 0; i < now; i++) - Dbprintf("EM4x50 tag data: " - _GREEN_("%08x") " (msb) - " _GREEN_("%08x") " (lsb)", - words[i], reflect32(words[i])); - } - } - } - - LOW(GPIO_SSC_DOUT); - lf_finalize(); - reply_ng(CMD_LF_EM4X50_WATCH, 1, 0, 0); -} - -//============================================================================== -// standalone mode functions -//============================================================================== - -int em4x50_standalone_brute(uint32_t start, uint32_t stop, uint32_t *pwd) { - - // envoke password search in standalone mode - - int status = false; - - em4x50_setup_read(); - - if (get_signalproperties() && find_em4x50_tag()) - status = brute(start, stop, pwd); - else - status = PM3_ETIMEOUT; - - lf_finalize(); - - return status; -} - -int em4x50_standalone_read(uint32_t *words) { - - int now = 0; - - em4x50_setup_read(); - - if (get_signalproperties() && find_em4x50_tag()) - if (find_double_listen_window(false)) - while (get_word_from_bitstream(&words[now]) == EM4X50_TAG_WORD) - now++; - - return now; + reply_ng(CMD_LF_EM4X50_WRITEPWD, status, 0, 0); } void em4x50_restore(em4x50_data_t *etd) { // restore em4x50 dump file to tag - bool bsuccess = false, blogin = false; - int res = 0; - int start_word = 0; - uint8_t status = 0; + bool bsuccess = false; + int status = PM3_EFAILED; + int start_word = 3; // first block/address with user data uint8_t em4x50_mem[DUMP_FILESIZE] = {0x0}; uint32_t addresses = 0x00001F01; // from fwr = 1 to lwr = 31 (0x1F) uint32_t words_client[EM4X50_NO_WORDS] = {0x0}; @@ -1290,18 +1349,19 @@ void em4x50_restore(em4x50_data_t *etd) { if (get_signalproperties() && find_em4x50_tag()) { // login first if password is available - if (etd->pwd_given) - blogin = (login(etd->password1) == PM3_SUCCESS); + if (etd->pwd_given) { + if (login(etd->password1) == PM3_SUCCESS) { + + // successful login allows words 1 and 2 to be written + start_word = 1; + } + } // write data to each address but ignore addresses // 0 -> password, 32 -> serial, 33 -> uid - - // without login words 1 and 2 cannot be written - start_word = (blogin) ? 1 : 3; - for (int i = start_word; i < EM4X50_NO_WORDS - 2; i++) { - res = write(words_client[i], i); - if (res == PM3_ETEAROFF) { + status = write(words_client[i], i); + if (status == PM3_ETEAROFF) { lf_finalize(); return; } @@ -1323,12 +1383,17 @@ void em4x50_restore(em4x50_data_t *etd) { } } - status = (bsuccess << 1) + blogin; + if (bsuccess) + status = PM3_SUCCESS; lf_finalize(); reply_ng(CMD_LF_EM4X50_RESTORE, status, 0, 0); } +//============================================================================== +// simulate functions +//============================================================================== + void em4x50_sim(void) { // simulate uploaded data in flash memory @@ -1385,85 +1450,38 @@ void em4x50_sim(void) { reply_ng(CMD_LF_EM4X50_SIM, status, 0, 0); } -void em4x50_stdread(void) { +//============================================================================== +// standalone mode functions +//============================================================================== - // reads data that tag transmits "voluntarily" -> standard read mode +int em4x50_standalone_brute(uint32_t start, uint32_t stop, uint32_t *pwd) { + + // envoke password search in standalone mode + + int status = false; + + em4x50_setup_read(); + + if (get_signalproperties() && find_em4x50_tag()) + status = brute(start, stop, pwd); + else + status = PM3_ETIMEOUT; + + lf_finalize(); + + return status; +} + +int em4x50_standalone_read(uint32_t *words) { int now = 0; - uint32_t words[EM4X50_NO_WORDS] = {0x0}; em4x50_setup_read(); - // set gHigh and gLow if (get_signalproperties() && find_em4x50_tag()) - standard_read(&now, words); - - LOW(GPIO_SSC_DOUT); - lf_finalize(); - reply_ng(CMD_LF_EM4X50_STDREAD, now, (uint8_t *)words, 4 * now); -} - -void em4x50_chk(uint32_t *offset) { - - // check passwords from dictionary content in flash memory - - int status = PM3_EFAILED; - uint8_t counter[2] = {0x00, 0x00}; - uint16_t isok = 0; - uint16_t pwd_count = 0; - uint16_t pwd_size_available = 0; - uint32_t pwd = 0x0; - uint8_t *pwds = BigBuf_get_EM_addr(); - - //----------------------------------------------------------------------------- - // Note: we call FpgaDownloadAndGo(FPGA_BITSTREAM_LF) here although FPGA is not - // involved in dealing with emulator memory. But if it is called later, it will - // destroy the Emulator Memory. - //----------------------------------------------------------------------------- - FpgaDownloadAndGo(FPGA_BITSTREAM_LF); - - BigBuf_Clear_EM(); - - // initialize passwords and get number of passwords - if (Flash_ReadData(*offset, counter, sizeof(counter)) != sizeof(counter)) - goto OUT; - - pwd_count = (uint16_t)(counter[1] << 8 | counter[0]); - if (pwd_count == 0) - goto OUT; - - pwd_size_available = 4 * pwd_count; - - isok = Flash_ReadData(*offset + 2, pwds, pwd_size_available); - if (isok != pwd_size_available) - goto OUT; - - em4x50_setup_read(); - - // set gHigh and gLow - if (get_signalproperties() && find_em4x50_tag()) { - - // try to login with current password - for (int i = 0; i < pwd_count; i++) { - - // manual interruption - if (BUTTON_PRESS()) { - status = BUTTON_SINGLE_CLICK; - break; - } - - // get next password - pwd = 0x0; - for (int j = 0; j < 4; j++) - pwd |= (*(pwds + 4 * i + j)) << ((3 - j) * 8); - - if ((status = login(pwd)) == PM3_SUCCESS) - break; - } - } - -OUT: - - lf_finalize(); - reply_ng(CMD_LF_EM4X50_CHK, status, (uint8_t *)&pwd, 32); + if (find_double_listen_window(false)) + while (get_word_from_bitstream(&words[now]) == EM4X50_TAG_WORD) + now++; + + return now; } diff --git a/client/src/cmdlfem4x50.c b/client/src/cmdlfem4x50.c index c0980e7f6..44e5297a8 100644 --- a/client/src/cmdlfem4x50.c +++ b/client/src/cmdlfem4x50.c @@ -16,99 +16,9 @@ #define CARD_MEMORY_SIZE 4096 -static int em4x50_load_file(const char *filename, uint8_t *data, size_t data_len, size_t *bytes_read) { - - // read data from dump file; file type is derived from file name extension - - int res = 0; - uint32_t serial = 0x0, device_id = 0x0; - - if (str_endswith(filename, ".eml")) - res = loadFileEML(filename, data, bytes_read) != PM3_SUCCESS; - else if (str_endswith(filename, ".json")) - res = loadFileJSON(filename, data, data_len, bytes_read, NULL); - else - res = loadFile(filename, ".bin", data, data_len, bytes_read); - - if ((res != PM3_SUCCESS) && (*bytes_read != DUMP_FILESIZE)) - return PM3_EFILE; - - // valid em4x50 data? - serial = bytes_to_num(data + 4 * EM4X50_DEVICE_SERIAL, 4); - device_id = bytes_to_num(data + 4 * EM4X50_DEVICE_ID, 4); - if (serial == device_id) { - PrintAndLogEx(WARNING, "No valid em4x50 data in file %s.", filename); - return PM3_ENODATA; - } - - return PM3_SUCCESS; -} - -static int em4x50_wipe_flash(int page) { - - int isok = 0; - - clearCommandBuffer(); - SendCommandMIX(CMD_FLASHMEM_WIPE, page, false, 0, NULL, 0); - PacketResponseNG resp; - - if (!WaitForResponseTimeout(CMD_ACK, &resp, 8000)) { - PrintAndLogEx(WARNING, "Timeout while waiting for reply."); - return PM3_ETIMEOUT; - } - - isok = resp.oldarg[0] & 0xFF; - if (!isok) { - PrintAndLogEx(WARNING, "Flash error"); - return PM3_EFLASH; - } - - return PM3_SUCCESS; -} - -static int em4x50_write_flash(uint8_t *data, int offset, size_t datalen) { - - int isok = 0; - uint32_t bytes_sent = 0; - uint32_t bytes_remaining = datalen; - uint32_t bytes_in_packet = 0; - PacketResponseNG resp; - - // wipe - em4x50_wipe_flash(0); - em4x50_wipe_flash(1); - em4x50_wipe_flash(2); - - // fast push mode - conn.block_after_ACK = true; - - while (bytes_remaining > 0) { - bytes_in_packet = MIN(FLASH_MEM_BLOCK_SIZE, bytes_remaining); - - clearCommandBuffer(); - SendCommandOLD(CMD_FLASHMEM_WRITE, offset + bytes_sent, bytes_in_packet, 0, data + bytes_sent, bytes_in_packet); - - bytes_remaining -= bytes_in_packet; - bytes_sent += bytes_in_packet; - - if (WaitForResponseTimeout(CMD_ACK, &resp, 2000) == false) { - PrintAndLogEx(WARNING, "timeout while waiting for reply."); - conn.block_after_ACK = false; - return PM3_ETIMEOUT; - } - - isok = resp.oldarg[0] & 0xFF; - if (!isok) { - conn.block_after_ACK = false; - PrintAndLogEx(FAILED, "Flash write fail [offset %u]", bytes_sent); - return PM3_EFLASH; - } - } - - conn.block_after_ACK = false; - - return PM3_SUCCESS; -} +//============================================================================== +// output functions +//============================================================================== static void prepare_result(const uint8_t *data, int fwr, int lwr, em4x50_word_t *words) { @@ -211,944 +121,102 @@ static void print_info_result(uint8_t *data) { PrintAndLogEx(NORMAL, ""); } -//quick test for EM4x50 tag -bool detect_4x50_block(void) { - em4x50_data_t etd = { - .pwd_given = false, - .addr_given = true, - .addresses = (EM4X50_DEVICE_ID << 8) | EM4X50_DEVICE_ID, - }; - em4x50_word_t words[EM4X50_NO_WORDS]; - return (em4x50_read(&etd, words) == PM3_SUCCESS); -} +//============================================================================== +// file/memory functions +//============================================================================== -int read_em4x50_uid(void) { - em4x50_data_t etd = { - .pwd_given = false, - .addr_given = true, - .addresses = (EM4X50_DEVICE_SERIAL << 8) | EM4X50_DEVICE_SERIAL, - }; - em4x50_word_t words[EM4X50_NO_WORDS]; - int res = em4x50_read(&etd, words); - if (res == PM3_SUCCESS) - PrintAndLogEx(INFO, " Serial: " _GREEN_("%s"), sprint_hex(words[EM4X50_DEVICE_SERIAL].byte, 4)); - return res; -} +static int em4x50_load_file(const char *filename, uint8_t *data, size_t data_len, size_t *bytes_read) { -int CmdEM4x50Info(const char *Cmd) { + // read data from dump file; file type is derived from file name extension - // envoke reading of a EM4x50 tag which has to be on the antenna because - // decoding is done by the device (not on client side) - - int pwdLen = 0; - uint8_t pwd[4] = {0x0}; - em4x50_data_t etd = {.pwd_given = false}; - - CLIParserContext *ctx; - CLIParserInit(&ctx, "lf em 4x50_info", - "Tag information EM4x50.", - "lf em 4x50_info\n" - "lf em 4x50_info -p 12345678 -> uses password 0x12345678\n" - ); - - void *argtable[] = { - arg_param_begin, - arg_str0("p", "passsword", "", "password, hex, 4 bytes, lsb"), - arg_param_end - }; - - CLIExecWithReturn(ctx, Cmd, argtable, true); - - CLIGetHexWithReturn(ctx, 1, pwd, &pwdLen); - - if (pwdLen) { - if (pwdLen != 4) { - PrintAndLogEx(FAILED, "password length must be 4 bytes instead of %d", pwdLen); - return PM3_EINVARG; - } else { - etd.password1 = (pwd[0] << 24) | (pwd[1] << 16) | (pwd[2] << 8) | pwd[3]; - etd.pwd_given = true; - } - } - - CLIParserFree(ctx); - - clearCommandBuffer(); - SendCommandNG(CMD_LF_EM4X50_INFO, (uint8_t *)&etd, sizeof(etd)); - - PacketResponseNG resp; - if (!WaitForResponseTimeout(CMD_LF_EM4X50_INFO, &resp, TIMEOUT)) { - PrintAndLogEx(WARNING, "Timeout while waiting for reply."); - return PM3_ETIMEOUT; - } - - bool login = resp.status & STATUS_LOGIN; - bool success = (resp.status & STATUS_SUCCESS) >> 1; - - if (success) { - if (etd.pwd_given) { - if (login == false) { - PrintAndLogEx(FAILED, "Login failed"); - return PM3_ESOFT; - } - PrintAndLogEx(SUCCESS, "Login with password " _YELLOW_("%08x"), etd.password1); - } - - print_info_result(resp.data.asBytes); - return PM3_SUCCESS; - } - - PrintAndLogEx(FAILED, "Reading tag " _RED_("failed")); - return PM3_ESOFT; -} - -int CmdEM4x50Write(const char *Cmd) { - - // envoke writing a single word (32 bit) to a EM4x50 tag - - int wordLen = 0, pwdLen = 0; - int addr = 0; - uint8_t word[4] = {0x0}; - uint8_t pwd[4] = {0x0}; - em4x50_data_t etd = {.pwd_given = false}; - - CLIParserContext *ctx; - CLIParserInit(&ctx, "lf em 4x50_write", - "Writes single block/word to EM4x50 tag.", - "lf em 4x50_write -b 3 -d 4f22e7ff -> writes 0x4f22e7ff to block 3\n" - "lf em 4x50_write -b 3 -d 4f22e7ff -p 12345678\n" - ); - - void *argtable[] = { - arg_param_begin, - arg_int1("b", "block", "
", "block/word address, dec"), - arg_str1("d", "data", "", "data, hex, 4 bytes, lsb"), - arg_str0("p", "passsword", "", "password, hex, 4 bytes, lsb"), - arg_param_end - }; - - CLIExecWithReturn(ctx, Cmd, argtable, true); - - addr = arg_get_int_def(ctx, 1, 0); - CLIGetHexWithReturn(ctx, 2, word, &wordLen); - CLIGetHexWithReturn(ctx, 3, pwd, &pwdLen); - - if (addr <= 0 || addr >= EM4X50_NO_WORDS) { - PrintAndLogEx(FAILED, "address has to be within range [0, 31]"); - return PM3_EINVARG; - } else { - etd.addresses = (addr << 8) | addr; - etd.addr_given = true; - } - if (wordLen != 4) { - PrintAndLogEx(FAILED, "word/data length must be 4 bytes instead of %d", wordLen); - return PM3_EINVARG; - } else { - etd.word = (word[0] << 24) | (word[1] << 16) | (word[2] << 8) | word[3]; - } - if (pwdLen) { - if (pwdLen != 4) { - PrintAndLogEx(FAILED, "password length must be 4 bytes instead of %d", pwdLen); - return PM3_EINVARG; - } else { - etd.password1 = (pwd[0] << 24) | (pwd[1] << 16) | (pwd[2] << 8) | pwd[3]; - etd.pwd_given = true; - } - } - - CLIParserFree(ctx); - - clearCommandBuffer(); - SendCommandNG(CMD_LF_EM4X50_WRITE, (uint8_t *)&etd, sizeof(etd)); - PacketResponseNG resp; - if (!WaitForResponseTimeout(CMD_LF_EM4X50_WRITE, &resp, TIMEOUT)) { - PrintAndLogEx(WARNING, "Timeout while waiting for reply."); - 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")); - return PM3_ESOFT; - } - - if (etd.pwd_given) { - bool login = resp.status & STATUS_LOGIN; - if (login == false) { - PrintAndLogEx(FAILED, "Login failed"); - return PM3_ESOFT; - } - PrintAndLogEx(SUCCESS, "Login with password " _YELLOW_("%08x"), etd.password1); - } - - // display result of writing operation in structured format - uint8_t *data = resp.data.asBytes; - em4x50_word_t words[EM4X50_NO_WORDS]; - - prepare_result(data, addr, addr, words); - print_result(words, addr, addr); - PrintAndLogEx(SUCCESS, "Successfully wrote to tag"); - PrintAndLogEx(HINT, "Try `" _YELLOW_("lf em 4x50_read a %u") "` - to read your data", addr); - return PM3_SUCCESS; -} - -int CmdEM4x50WritePwd(const char *Cmd) { - - // envokes changing the password of EM4x50 tag - - int status = PM3_EFAILED; - int pwdLen = 0, npwdLen = 0; - uint8_t pwd[4] = {0x0}, npwd[4] = {0x0}; - PacketResponseNG resp; - em4x50_data_t etd; - - CLIParserContext *ctx; - CLIParserInit(&ctx, "lf em 4x50_writepwd", - "Writes EM4x50 password.", - "lf em 4x50_writepwd -p 4f22e7ff -n 12345678 -> replaces password 0x4f22e7ff with 0x12345678\n" - ); - - void *argtable[] = { - arg_param_begin, - arg_str1("p", "pwd", "", "password, hex, 4 bytes, lsb"), - arg_str1("n", "newpwd", "", "new password, hex, 4 bytes, lsb"), - arg_param_end - }; - - CLIExecWithReturn(ctx, Cmd, argtable, true); - - CLIGetHexWithReturn(ctx, 1, pwd, &pwdLen); - CLIGetHexWithReturn(ctx, 2, npwd, &npwdLen); - - if (pwdLen != 4) { - PrintAndLogEx(FAILED, "password length must be 4 bytes instead of %d", pwdLen); - return PM3_EINVARG; - } else { - etd.password1 = (pwd[0] << 24) | (pwd[1] << 16) | (pwd[2] << 8) | pwd[3]; - } - if (npwdLen != 4) { - PrintAndLogEx(FAILED, "password length must be 4 bytes instead of %d", npwdLen); - return PM3_EINVARG; - } else { - etd.password2 = (npwd[0] << 24) | (npwd[1] << 16) | (npwd[2] << 8) | npwd[3]; - } - - CLIParserFree(ctx); - - clearCommandBuffer(); - SendCommandNG(CMD_LF_EM4X50_WRITEPWD, (uint8_t *)&etd, sizeof(etd)); - - if (!WaitForResponseTimeout(CMD_LF_EM4X50_WRITEPWD, &resp, TIMEOUT)) { - PrintAndLogEx(WARNING, "Timeout while waiting for reply."); - return PM3_ETIMEOUT; - } - - status = resp.status; - - if (status == PM3_ETEAROFF) - return PM3_SUCCESS; - - // print response - if (status != PM3_SUCCESS) { - PrintAndLogEx(FAILED, "Writing password " _RED_("failed")); - return PM3_EFAILED; - } - - PrintAndLogEx(SUCCESS, "Writing new password " _GREEN_("ok")); - - return PM3_SUCCESS; -} - -int em4x50_read(em4x50_data_t *etd, em4x50_word_t *out) { - - // envoke reading - // - with given address (option b) (and optional password if address is - // read protected) -> selective read mode - - em4x50_data_t edata = { .pwd_given = false, .addr_given = false }; - - if (etd != NULL) { - edata = *etd; - } - - clearCommandBuffer(); - SendCommandNG(CMD_LF_EM4X50_READ, (uint8_t *)&edata, sizeof(edata)); - - PacketResponseNG resp; - if (!WaitForResponseTimeout(CMD_LF_EM4X50_READ, &resp, TIMEOUT)) { - PrintAndLogEx(WARNING, "(em4x50) timeout while waiting for reply."); - return PM3_ETIMEOUT; - } - - bool isOK = (resp.status & STATUS_SUCCESS) >> 1; - if (isOK == false) - return PM3_ESOFT; - - if (edata.pwd_given) { - bool login = resp.status & STATUS_LOGIN; - if (login == false) { - PrintAndLogEx(FAILED, "Login failed"); - return PM3_ESOFT; - } - PrintAndLogEx(SUCCESS, "Login with password " _YELLOW_("%08x"), etd->password1); - } - - uint8_t *data = resp.data.asBytes; - em4x50_word_t words[EM4X50_NO_WORDS]; - prepare_result(data, etd->addresses & 0xFF, (etd->addresses >> 8) & 0xFF, words); - - if (out != NULL) { - memcpy(out, &words, sizeof(em4x50_word_t) * EM4X50_NO_WORDS); - } - - print_result(words, etd->addresses & 0xFF, (etd->addresses >> 8) & 0xFF); - - return PM3_SUCCESS; -} - -int CmdEM4x50Read(const char *Cmd) { - - int pwdLen = 0; - int addr = 0; - uint8_t pwd[4] = {0x0}; - em4x50_data_t etd; - - // init - memset(&etd, 0x00, sizeof(em4x50_data_t)); - etd.addr_given = false; - etd.pwd_given = false; - - CLIParserContext *ctx; - CLIParserInit(&ctx, "lf em 4x50_read", - "Reads single EM4x50 block/word.", - "lf em 4x50_read -b 3 -> reads block 3\n" - "lf em 4x50_read -b 32 -p 12345678 -> reads block 32 with password 0x12345678\n" - ); - - void *argtable[] = { - arg_param_begin, - arg_int1("b", "block", "
", "block/word address, dec"), - arg_str0("p", "passsword", "", "password, hex, 4 bytes, lsb"), - arg_param_end - }; - - CLIExecWithReturn(ctx, Cmd, argtable, true); - - addr = arg_get_int_def(ctx, 1, 0); - CLIGetHexWithReturn(ctx, 2, pwd, &pwdLen); - - if (addr <= 0 || addr >= EM4X50_NO_WORDS) { - return PM3_EINVARG; - } else { - etd.addresses = (addr << 8) | addr; - etd.addr_given = true; - } - - if (pwdLen) { - if (pwdLen != 4) { - PrintAndLogEx(FAILED, "password length must be 4 bytes instead of %d", pwdLen); - return PM3_EINVARG; - } else { - etd.password1 = (pwd[0] << 24) | (pwd[1] << 16) | (pwd[2] << 8) | pwd[3]; - etd.pwd_given = true; - } - } - - CLIParserFree(ctx); - - return em4x50_read(&etd, NULL); -} - -int CmdEM4x50Dump(const char *Cmd) { - - int fnLen = 0, pwdLen = 0; - uint8_t pwd[4] = {0x0}; - char filename[FILE_PATH_SIZE] = {0}; - char *fptr = filename; - em4x50_data_t etd = {.pwd_given = false}; - uint8_t data[DUMP_FILESIZE] = {0}; - - CLIParserContext *ctx; - CLIParserInit(&ctx, "lf em 4x50_dump", - "Reads all blocks/words from EM4x50 tag and saves dump in bin/eml/json format.", - "lf em 4x50_dump -> saves dump in lf-4x50--dump.bin/eml/json\n" - "lf em 4x50_dump -f mydump.eml -> saves dump in mydump.eml\n" - "lf em 4x50_dump -p 12345678\n" - "lf em 4x50_dump -f mydump.eml -p 12345678\n" - ); - - void *argtable[] = { - arg_param_begin, - arg_str0("f", "filename", "", "dump filename (bin/eml/json)"), - arg_str0("p", "passsword", "", "password, hex, 4 bytes, lsb"), - arg_param_end - }; - - CLIExecWithReturn(ctx, Cmd, argtable, true); - - CLIParamStrToBuf(arg_get_str(ctx, 1), - (uint8_t *)filename, - FILE_PATH_SIZE, - &fnLen - ); - CLIGetHexWithReturn(ctx, 2, pwd, &pwdLen); - - if (pwdLen) { - if (pwdLen != 4) { - PrintAndLogEx(FAILED, "password length must be 4 bytes instead of %d", pwdLen); - return PM3_EINVARG; - } else { - etd.password1 = (pwd[0] << 24) | (pwd[1] << 16) | (pwd[2] << 8) | pwd[3]; - etd.pwd_given = true; - } - } - - CLIParserFree(ctx); - - PrintAndLogEx(INFO, "Reading EM4x50 tag"); - clearCommandBuffer(); - SendCommandNG(CMD_LF_EM4X50_INFO, (uint8_t *)&etd, sizeof(etd)); - PacketResponseNG resp; - if (!WaitForResponseTimeout(CMD_LF_EM4X50_INFO, &resp, TIMEOUT)) { - PrintAndLogEx(WARNING, "Timeout while waiting for reply."); - return PM3_ETIMEOUT; - } - - bool success = (resp.status & STATUS_SUCCESS) >> 1; - if (success == false) { - PrintAndLogEx(FAILED, "Reading tag " _RED_("failed")); - return PM3_ESOFT; - } - - // structured format - em4x50_word_t words[EM4X50_NO_WORDS]; - prepare_result(resp.data.asBytes, 0, EM4X50_NO_WORDS - 1, words); - - // result output - PrintAndLogEx(INFO, _YELLOW_("EM4x50 data:")); - print_result(words, 0, EM4X50_NO_WORDS - 1); - - // user supplied filename? - if (fnLen == 0) { - PrintAndLogEx(INFO, "Using UID as filename"); - fptr += sprintf(fptr, "lf-4x50-"); - FillFileNameByUID(fptr, words[EM4X50_DEVICE_ID].byte, "-dump", 4); - } - - for (int i = 0; i < EM4X50_NO_WORDS; i++) - memcpy(data + (i * 4), words[i].byte, 4); - - // saveFileEML will add .eml extension to filename - // saveFile (binary) passes in the .bin extension. - // saveFileJSON adds .json extension - saveFileEML(filename, data, sizeof(data), 4); - saveFile(filename, ".bin", data, sizeof(data)); - saveFileJSON(filename, jsfEM4x50, data, sizeof(data), NULL); - - return PM3_SUCCESS; -} - -int CmdEM4x50Wipe(const char *Cmd) { - - // fills EM4x50 tag with zeros including password - - bool isOK = false; - int pwdLen = 0; - uint8_t pwd[4] = {0x0}; - em4x50_data_t etd = {.pwd_given = false, .word = 0x0, .password2 = 0x0}; - PacketResponseNG resp; - - CLIParserContext *ctx; - CLIParserInit(&ctx, "lf em 4x50_wipe", - "Wipes EM4x50 tag.", - "lf em 4x50_wipe -p 12345678 -> wipes tag with password 0x12345678\n" - ); - - void *argtable[] = { - arg_param_begin, - arg_str1("p", "passsword", "", "password, hex, 4 bytes, lsb"), - arg_param_end - }; - - CLIExecWithReturn(ctx, Cmd, argtable, true); - - CLIGetHexWithReturn(ctx, 1, pwd, &pwdLen); - if (pwdLen != 4) { - PrintAndLogEx(FAILED, "password length must be 4 bytes instead of %d", pwdLen); - return PM3_EINVARG; - } else { - etd.password1 = (pwd[0] << 24) | (pwd[1] << 16) | (pwd[2] << 8) | pwd[3]; - etd.pwd_given = true; - } - - CLIParserFree(ctx); - - // clear password - clearCommandBuffer(); - SendCommandNG(CMD_LF_EM4X50_WRITEPWD, (uint8_t *)&etd, sizeof(etd)); - if (!WaitForResponseTimeout(CMD_LF_EM4X50_WRITEPWD, &resp, TIMEOUT)) { - PrintAndLogEx(WARNING, "Timeout while waiting for reply."); - return PM3_ETIMEOUT; - } - - if (resp.status == PM3_SUCCESS) { - PrintAndLogEx(SUCCESS, "Resetting password " _GREEN_("ok")); - } else { - PrintAndLogEx(FAILED, "Resetting password " _RED_("failed")); - return PM3_ESOFT; - } - - // from now on new password 0x0 - etd.password1 = 0x0; - - // clear data (words 1 to 31) - for (int i = 1; i < EM4X50_DEVICE_SERIAL; i++) { - - // no login necessary for blocks 3 to 31 - etd.pwd_given = (i <= EM4X50_CONTROL); - - PrintAndLogEx(INPLACE, "Wiping block %i", i); - - etd.addresses = i << 8 | i; - clearCommandBuffer(); - SendCommandNG(CMD_LF_EM4X50_WRITE, (uint8_t *)&etd, sizeof(etd)); - if (!WaitForResponseTimeout(CMD_LF_EM4X50_WRITE, &resp, TIMEOUT)) { - PrintAndLogEx(WARNING, "Timeout while waiting for reply."); - return PM3_ETIMEOUT; - } - - isOK = resp.status; - if (!isOK) { - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(FAILED, "Wiping data " _RED_("failed")); - return PM3_ESOFT; - } - } - - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(SUCCESS, "Wiping data " _GREEN_("ok")); - - PrintAndLogEx(INFO, "Done"); - - return PM3_SUCCESS; -} - -int CmdEM4x50Brute(const char *Cmd) { - - const int speed = 27; // 27 passwords/second (empirical value) - int no_iter = 0, dur_h = 0, dur_m = 0, dur_s = 0; - - int pwd1Len = 0, pwd2Len = 0; - uint8_t pwd1[4] = {0x0}, pwd2[4] = {0x0}; - em4x50_data_t etd; - PacketResponseNG resp; - - CLIParserContext *ctx; - CLIParserInit(&ctx, "lf em 4x50_brute", - "Tries to bruteforce the password of a EM4x50. Function can be stopped by pressing pm3 button.", - "lf em 4x50_brute -f 12330000 -l 12340000 -> tries passwords from 0x12330000 to 0x1234000000\n" - ); - - void *argtable[] = { - arg_param_begin, - arg_str1("f", "fp", "", "first password (start), hex, 4 bytes, lsb"), - arg_str1("l", "lp", "", "last password (stop), hex, 4 bytes, lsb"), - arg_param_end - }; - - CLIExecWithReturn(ctx, Cmd, argtable, true); - - CLIGetHexWithReturn(ctx, 1, pwd1, &pwd1Len); - CLIGetHexWithReturn(ctx, 2, pwd2, &pwd2Len); - - if (pwd1Len != 4) { - PrintAndLogEx(FAILED, "password length must be 4 bytes instead of %d", pwd1Len); - return PM3_EINVARG; - } else if (pwd2Len != 4) { - PrintAndLogEx(FAILED, "password length must be 4 bytes instead of %d", pwd2Len); - return PM3_EINVARG; - } else { - etd.password1 = (pwd1[0] << 24) | (pwd1[1] << 16) | (pwd1[2] << 8) | pwd1[3]; - etd.password2 = (pwd2[0] << 24) | (pwd2[1] << 16) | (pwd2[2] << 8) | pwd2[3]; - } - - CLIParserFree(ctx); - - // print some information - no_iter = etd.password2 - etd.password1 + 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(INFO, "Trying %i passwords in range [0x%08x, 0x%08x]", - no_iter, etd.password1, etd.password2); - PrintAndLogEx(INFO, "Estimated duration: %ih%im%is", dur_h, dur_m, dur_s); - - // start - clearCommandBuffer(); - SendCommandNG(CMD_LF_EM4X50_BRUTE, (uint8_t *)&etd, sizeof(etd)); - WaitForResponse(CMD_LF_EM4X50_BRUTE, &resp); - - // print response - if ((bool)resp.status) - PrintAndLogEx(SUCCESS, "Password " _GREEN_("found") ": 0x%08x", resp.data.asDwords[0]); + int res = 0; + uint32_t serial = 0x0, device_id = 0x0; + + if (str_endswith(filename, ".eml")) + res = loadFileEML(filename, data, bytes_read) != PM3_SUCCESS; + else if (str_endswith(filename, ".json")) + res = loadFileJSON(filename, data, data_len, bytes_read, NULL); else - PrintAndLogEx(FAILED, "Password: " _RED_("not found")); + res = loadFile(filename, ".bin", data, data_len, bytes_read); - return PM3_SUCCESS; -} - -int CmdEM4x50Login(const char *Cmd) { - - int pwdLen = 0; - uint8_t pwd[4] = {0x0}; - uint32_t password = 0x0; - PacketResponseNG resp; - - CLIParserContext *ctx; - CLIParserInit(&ctx, "lf em 4x50_login", - "Login into EM4x50 tag.", - "lf em 4x50_login -p 12345678 -< login with password 12345678\n" - ); - - void *argtable[] = { - arg_param_begin, - arg_str1("p", "passsword", "", "password, hex, 4 bytes, lsb"), - arg_param_end - }; - - CLIExecWithReturn(ctx, Cmd, argtable, true); - - CLIGetHexWithReturn(ctx, 1, pwd, &pwdLen); - if (pwdLen != 4) { - PrintAndLogEx(FAILED, "password length must be 4 bytes instead of %d", pwdLen); - return PM3_EINVARG; - } else { - password = (pwd[0] << 24) | (pwd[1] << 16) | (pwd[2] << 8) | pwd[3]; - } - - CLIParserFree(ctx); - - // start - clearCommandBuffer(); - SendCommandNG(CMD_LF_EM4X50_LOGIN, (uint8_t *)&password, sizeof(password)); - WaitForResponse(CMD_LF_EM4X50_LOGIN, &resp); - - // print response - if (resp.status == PM3_SUCCESS) - PrintAndLogEx(SUCCESS, "Login " _GREEN_("ok")); - else - PrintAndLogEx(FAILED, "Login " _RED_("failed")); - - return resp.status; -} - -int CmdEM4x50Reset(const char *Cmd) { - - PacketResponseNG resp; - - CLIParserContext *ctx; - CLIParserInit(&ctx, "lf em 4x50_reset", - "Reseta EM4x50 tag.", - "lf em 4x50_reset\n" - ); - - void *argtable[] = { - arg_param_begin, - arg_param_end - }; - - CLIExecWithReturn(ctx, Cmd, argtable, true); - CLIParserFree(ctx); - - // start - clearCommandBuffer(); - SendCommandNG(CMD_LF_EM4X50_RESET, 0, 0); - WaitForResponse(CMD_LF_EM4X50_RESET, &resp); - - // print response - if (resp.status == PM3_SUCCESS) - PrintAndLogEx(SUCCESS, "Reset " _GREEN_("ok")); - else - PrintAndLogEx(FAILED, "Reset " _RED_("failed")); - - return resp.status; -} - -int CmdEM4x50Watch(const char *Cmd) { - - // continously envoke reading of a EM4x50 tag - - PacketResponseNG resp; - - CLIParserContext *ctx; - CLIParserInit(&ctx, "lf em 4x50_watch", - "Watches for EM4x50 tags. Function runs until button is pressed.", - "lf em 4x50_watch\n" - ); - - void *argtable[] = { - arg_param_begin, - arg_param_end - }; - - CLIExecWithReturn(ctx, Cmd, argtable, true); - CLIParserFree(ctx); - - PrintAndLogEx(SUCCESS, "Watching for EM4x50 cards - place tag on antenna"); - PrintAndLogEx(INFO, "You can cancel this operation by pressing the pm3 button"); - - clearCommandBuffer(); - SendCommandNG(CMD_LF_EM4X50_WATCH, 0, 0); - WaitForResponseTimeoutW(CMD_LF_EM4X50_WATCH, &resp, -1, false); - - PrintAndLogEx(INFO, "Done"); - return PM3_SUCCESS; -} - -int CmdEM4x50Restore(const char *Cmd) { - - int uidLen = 0, fnLen = 0, pwdLen = 0, res = 0; - uint8_t pwd[4] = {0x0}, uid[4] = {0x0}; - size_t bytes_read = 0; - char filename[FILE_PATH_SIZE] = {0}; - em4x50_data_t etd = {.pwd_given = false}; - uint8_t data[DUMP_FILESIZE] = {0x0}; - - CLIParserContext *ctx; - CLIParserInit(&ctx, "lf em 4x50_restore", - "Restores data from dumpfile onto a Em4x50 tag.", - "lf em 4x50_restore -u 1b5aff5c -> uses lf-4x50-1B5AFF5C-dump.bin\n" - "lf em 4x50_restore -f mydump.eml -> uses mydump.eml\n" - "lf em 4x50_restore -u 1b5aff5c -p 12345678 -> \n" - "lf em 4x50_restore -f mydump.eml -p 12345678 -> \n" - ); - - void *argtable[] = { - arg_param_begin, - arg_str0("u", "uid", "", "uid, hex, 4 bytes, msb, restore from lf-4x50--dump.bin"), - arg_str0("f", "filename", "", "dump filename (bin/eml/json)"), - arg_str0("p", "passsword", "", "password, hex, 4 bytes, lsb"), - arg_param_end - }; - - CLIExecWithReturn(ctx, Cmd, argtable, true); - - CLIGetHexWithReturn(ctx, 1, uid, &uidLen); - CLIParamStrToBuf(arg_get_str(ctx, 2), - (uint8_t *)filename, - FILE_PATH_SIZE, - &fnLen - ); - CLIGetHexWithReturn(ctx, 3, pwd, &pwdLen); - - if ((uidLen && fnLen) || (!uidLen && !fnLen)) { - PrintAndLogEx(FAILED, "either use option 'u' or option 'f'"); - return PM3_EINVARG; - } - - if (uidLen) { - snprintf(filename, FILE_PATH_SIZE, "./lf-4x50-%02x%02x%02x%02x-dump.bin", - uid[0], - uid[1], - uid[2], - uid[3] - ); - } - - if (pwdLen) { - if (pwdLen != 4) { - PrintAndLogEx(FAILED, "password length must be 4 bytes instead of %d", pwdLen); - return PM3_EINVARG; - } else { - etd.password1 = (pwd[0] << 24) | (pwd[1] << 16) | (pwd[2] << 8) | pwd[3]; - etd.pwd_given = true; - } - } - - CLIParserFree(ctx); - - PrintAndLogEx(INFO, "Restoring " _YELLOW_("%s")" to card", filename); - - // read data from dump file; file type has to be "bin", "eml" or "json" - if (em4x50_load_file(filename, data, DUMP_FILESIZE, &bytes_read) != PM3_SUCCESS) + if ((res != PM3_SUCCESS) && (*bytes_read != DUMP_FILESIZE)) return PM3_EFILE; - // upload to flash memory - res = em4x50_write_flash(data, 0, bytes_read); - if (res != PM3_SUCCESS) { - PrintAndLogEx(WARNING, "Error uploading to flash."); - return res; - } - - clearCommandBuffer(); - SendCommandNG(CMD_LF_EM4X50_RESTORE, (uint8_t *)&etd, sizeof(etd)); - PacketResponseNG resp; - if (!WaitForResponseTimeout(CMD_LF_EM4X50_RESTORE, &resp, 2 * TIMEOUT)) { - PrintAndLogEx(FAILED, "Timeout while waiting for reply."); - return PM3_ETIMEOUT; - } - - if (resp.status == PM3_ETEAROFF) - return PM3_SUCCESS; - - bool isOK = (resp.status & STATUS_SUCCESS) >> 1; - if (isOK == false) { - PrintAndLogEx(FAILED, "Restore " _RED_("failed")); - return PM3_ESOFT; - } - - if (etd.pwd_given) { - bool login = resp.status & STATUS_LOGIN; - if (login == false) { - PrintAndLogEx(FAILED, "Login failed"); - return PM3_ESOFT; - } - PrintAndLogEx(SUCCESS, "Login with password " _YELLOW_("%08x"), etd.password1); - } - PrintAndLogEx(SUCCESS, "Restore " _GREEN_("ok")); - PrintAndLogEx(INFO, "Finished restoring"); - - return PM3_SUCCESS; -} - -int CmdEM4x50Sim(const char *Cmd) { - - int slen = 0, res = 0; - size_t bytes_read = 0; - uint8_t data[DUMP_FILESIZE] = {0x0}; - char filename[FILE_PATH_SIZE] = {0}; - PacketResponseNG resp; - - CLIParserContext *ctx; - CLIParserInit(&ctx, "lf em 4x50_sim", - "Simulates a EM4x50 tag", - "lf em 4x50_sim -> simulates EM4x50 data in flash memory.\n" - "lf em 4x50_sim -f mydump.eml -> simulates content of file ./mydump\n" - ); - - void *argtable[] = { - arg_param_begin, - arg_str0("f", "filename", "", "dump filename, bin/eml/json"), - arg_param_end - }; - - CLIExecWithReturn(ctx, Cmd, argtable, true); - CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &slen); - CLIParserFree(ctx); - - // read data from dump file; file type has to be "bin", "eml" or "json" - if (slen != 0) { - - // load file content - if (em4x50_load_file(filename, data, DUMP_FILESIZE, &bytes_read) != PM3_SUCCESS) { - PrintAndLogEx(FAILED, "Read error"); - return PM3_EFILE; - } - - if (bytes_read * 8 > FLASH_MEM_MAX_SIZE) { - PrintAndLogEx(FAILED, "Filesize is larger than available memory"); - return PM3_EOVFLOW; - } - - PrintAndLogEx(INFO, "Uploading dump " _YELLOW_("%s") " to flash memory", filename); - - if (res != PM3_SUCCESS) { - PrintAndLogEx(WARNING, "Error wiping flash."); - return res; - } - - // upload to device - res = em4x50_write_flash(data, 0, bytes_read); - if (res != PM3_SUCCESS) { - PrintAndLogEx(WARNING, "Error uploading to flash."); - return res; - } - } - - PrintAndLogEx(INFO, "Simulating data in " _YELLOW_("%s"), filename); - - clearCommandBuffer(); - SendCommandNG(CMD_LF_EM4X50_SIM, 0, 0); - - WaitForResponse(CMD_LF_EM4X50_SIM, &resp); - - if (resp.status == PM3_ETEAROFF) { - return PM3_SUCCESS; - } else if (resp.status == PM3_ENODATA) { - PrintAndLogEx(FAILED, "No valid em4x50 data in flash memory."); + // valid em4x50 data? + serial = bytes_to_num(data + 4 * EM4X50_DEVICE_SERIAL, 4); + device_id = bytes_to_num(data + 4 * EM4X50_DEVICE_ID, 4); + if (serial == device_id) { + PrintAndLogEx(WARNING, "No valid em4x50 data in file %s.", filename); return PM3_ENODATA; } - PrintAndLogEx(INFO, "Done"); return PM3_SUCCESS; } -int CmdEM4x50StdRead(const char *Cmd) { - - int now = 0, status = PM3_EFAILED; - PacketResponseNG resp; - - CLIParserContext *ctx; - CLIParserInit(&ctx, "lf em 4x50_stdread", - "Shows standard read data of EM4x50 tag.", - "lf em 4x50_stdread\n" - ); - - void *argtable[] = { - arg_param_begin, - arg_param_end - }; - - CLIExecWithReturn(ctx, Cmd, argtable, true); - CLIParserFree(ctx); - - // start +static int em4x50_wipe_flash(int page) { + + int isok = 0; + clearCommandBuffer(); - SendCommandNG(CMD_LF_EM4X50_STDREAD, 0, 0); - if (!WaitForResponseTimeout(CMD_LF_EM4X50_STDREAD, &resp, TIMEOUT)) { + SendCommandMIX(CMD_FLASHMEM_WIPE, page, false, 0, NULL, 0); + PacketResponseNG resp; + + if (!WaitForResponseTimeout(CMD_ACK, &resp, 8000)) { PrintAndLogEx(WARNING, "Timeout while waiting for reply."); return PM3_ETIMEOUT; } - - now = resp.status; - // print response - if (now > 0) { + isok = resp.oldarg[0] & 0xFF; + if (!isok) { + PrintAndLogEx(WARNING, "Flash error"); + return PM3_EFLASH; + } + + return PM3_SUCCESS; +} - em4x50_word_t words[EM4X50_NO_WORDS]; - - prepare_result(resp.data.asBytes, 0, now - 1, words); +static int em4x50_write_flash(uint8_t *data, int offset, size_t datalen) { + + int isok = 0; + uint32_t bytes_sent = 0; + uint32_t bytes_remaining = datalen; + uint32_t bytes_in_packet = 0; + PacketResponseNG resp; - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, " # | word (msb) | word (lsb) "); - PrintAndLogEx(INFO, "----+-------------+-------------"); + // wipe + em4x50_wipe_flash(0); + em4x50_wipe_flash(1); + em4x50_wipe_flash(2); - for (int i = 0; i < now; i++) { + // fast push mode + conn.block_after_ACK = true; - char r[30] = {0}; - for (int j = 3; j >= 0; j--) - sprintf(r + strlen(r), "%02x ", reflect8(words[i].byte[j])); + while (bytes_remaining > 0) { + bytes_in_packet = MIN(FLASH_MEM_BLOCK_SIZE, bytes_remaining); - PrintAndLogEx(INFO, " %2i | " _GREEN_("%s") "| %s", - i, - sprint_hex(words[i].byte, 4), - r - ); + clearCommandBuffer(); + SendCommandOLD(CMD_FLASHMEM_WRITE, offset + bytes_sent, bytes_in_packet, 0, data + bytes_sent, bytes_in_packet); + + bytes_remaining -= bytes_in_packet; + bytes_sent += bytes_in_packet; + + if (WaitForResponseTimeout(CMD_ACK, &resp, 2000) == false) { + PrintAndLogEx(WARNING, "timeout while waiting for reply."); + conn.block_after_ACK = false; + return PM3_ETIMEOUT; } - - status = PM3_SUCCESS; - PrintAndLogEx(INFO, "----+-------------+-------------"); - PrintAndLogEx(SUCCESS, "Standard read " _GREEN_("ok")); - } else { - PrintAndLogEx(FAILED, "Standard read " _RED_("failed")); + isok = resp.oldarg[0] & 0xFF; + if (!isok) { + conn.block_after_ACK = false; + PrintAndLogEx(FAILED, "Flash write fail [offset %u]", bytes_sent); + return PM3_EFLASH; + } } - return status; + conn.block_after_ACK = false; + + return PM3_SUCCESS; } int CmdEM4x50ELoad(const char *Cmd) { @@ -1249,6 +317,120 @@ int CmdEM4x50ESave(const char *Cmd) { return PM3_SUCCESS; } +//============================================================================== +// login functions +//============================================================================== + +int CmdEM4x50Login(const char *Cmd) { + + int pwdLen = 0; + uint8_t pwd[4] = {0x0}; + uint32_t password = 0x0; + PacketResponseNG resp; + + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf em 4x50_login", + "Login into EM4x50 tag.", + "lf em 4x50_login -p 12345678 -< login with password 12345678\n" + ); + + void *argtable[] = { + arg_param_begin, + arg_str1("p", "passsword", "", "password, hex, 4 bytes, lsb"), + arg_param_end + }; + + CLIExecWithReturn(ctx, Cmd, argtable, true); + + CLIGetHexWithReturn(ctx, 1, pwd, &pwdLen); + if (pwdLen != 4) { + PrintAndLogEx(FAILED, "password length must be 4 bytes instead of %d", pwdLen); + return PM3_EINVARG; + } else { + password = (pwd[0] << 24) | (pwd[1] << 16) | (pwd[2] << 8) | pwd[3]; + } + + CLIParserFree(ctx); + + // start + clearCommandBuffer(); + SendCommandNG(CMD_LF_EM4X50_LOGIN, (uint8_t *)&password, sizeof(password)); + WaitForResponse(CMD_LF_EM4X50_LOGIN, &resp); + + // print response + if (resp.status == PM3_SUCCESS) + PrintAndLogEx(SUCCESS, "Login " _GREEN_("ok")); + else + PrintAndLogEx(FAILED, "Login " _RED_("failed")); + + return resp.status; +} + +int CmdEM4x50Brute(const char *Cmd) { + + const int speed = 27; // 27 passwords/second (empirical value) + int no_iter = 0, dur_h = 0, dur_m = 0, dur_s = 0; + + int pwd1Len = 0, pwd2Len = 0; + uint8_t pwd1[4] = {0x0}, pwd2[4] = {0x0}; + em4x50_data_t etd; + PacketResponseNG resp; + + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf em 4x50_brute", + "Tries to bruteforce the password of a EM4x50. Function can be stopped by pressing pm3 button.", + "lf em 4x50_brute -f 12330000 -l 12340000 -> tries passwords from 0x12330000 to 0x1234000000\n" + ); + + void *argtable[] = { + arg_param_begin, + arg_str1("f", "fp", "", "first password (start), hex, 4 bytes, lsb"), + arg_str1("l", "lp", "", "last password (stop), hex, 4 bytes, lsb"), + arg_param_end + }; + + CLIExecWithReturn(ctx, Cmd, argtable, true); + + CLIGetHexWithReturn(ctx, 1, pwd1, &pwd1Len); + CLIGetHexWithReturn(ctx, 2, pwd2, &pwd2Len); + + if (pwd1Len != 4) { + PrintAndLogEx(FAILED, "password length must be 4 bytes instead of %d", pwd1Len); + return PM3_EINVARG; + } else if (pwd2Len != 4) { + PrintAndLogEx(FAILED, "password length must be 4 bytes instead of %d", pwd2Len); + return PM3_EINVARG; + } else { + etd.password1 = (pwd1[0] << 24) | (pwd1[1] << 16) | (pwd1[2] << 8) | pwd1[3]; + etd.password2 = (pwd2[0] << 24) | (pwd2[1] << 16) | (pwd2[2] << 8) | pwd2[3]; + } + + CLIParserFree(ctx); + + // print some information + no_iter = etd.password2 - etd.password1 + 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(INFO, "Trying %i passwords in range [0x%08x, 0x%08x]", + no_iter, etd.password1, etd.password2); + PrintAndLogEx(INFO, "Estimated duration: %ih%im%is", dur_h, dur_m, dur_s); + + // start + clearCommandBuffer(); + SendCommandNG(CMD_LF_EM4X50_BRUTE, (uint8_t *)&etd, sizeof(etd)); + WaitForResponse(CMD_LF_EM4X50_BRUTE, &resp); + + // print response + if ((bool)resp.status) + PrintAndLogEx(SUCCESS, "Password " _GREEN_("found") ": 0x%08x", resp.data.asDwords[0]); + else + PrintAndLogEx(FAILED, "Password: " _RED_("not found")); + + return PM3_SUCCESS; +} + int CmdEM4x50Chk(const char *Cmd) { // upload passwords from given dictionary to flash memory and @@ -1364,3 +546,813 @@ int CmdEM4x50Chk(const char *Cmd) { PrintAndLogEx(INFO, "Done"); return PM3_SUCCESS; } + +//============================================================================== +// reset functions +//============================================================================== + +int CmdEM4x50Reset(const char *Cmd) { + + PacketResponseNG resp; + + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf em 4x50_reset", + "Reseta EM4x50 tag.", + "lf em 4x50_reset\n" + ); + + void *argtable[] = { + arg_param_begin, + arg_param_end + }; + + CLIExecWithReturn(ctx, Cmd, argtable, true); + CLIParserFree(ctx); + + // start + clearCommandBuffer(); + SendCommandNG(CMD_LF_EM4X50_RESET, 0, 0); + WaitForResponse(CMD_LF_EM4X50_RESET, &resp); + + // print response + if (resp.status == PM3_SUCCESS) + PrintAndLogEx(SUCCESS, "Reset " _GREEN_("ok")); + else + PrintAndLogEx(FAILED, "Reset " _RED_("failed")); + + return resp.status; +} + +//============================================================================== +// read functions +//============================================================================== + +//quick test for EM4x50 tag +bool detect_4x50_block(void) { + em4x50_data_t etd = { + .pwd_given = false, + .addr_given = true, + .addresses = (EM4X50_DEVICE_ID << 8) | EM4X50_DEVICE_ID, + }; + em4x50_word_t words[EM4X50_NO_WORDS]; + return (em4x50_read(&etd, words) == PM3_SUCCESS); +} + +int read_em4x50_uid(void) { + em4x50_data_t etd = { + .pwd_given = false, + .addr_given = true, + .addresses = (EM4X50_DEVICE_SERIAL << 8) | EM4X50_DEVICE_SERIAL, + }; + em4x50_word_t words[EM4X50_NO_WORDS]; + int res = em4x50_read(&etd, words); + if (res == PM3_SUCCESS) + PrintAndLogEx(INFO, " Serial: " _GREEN_("%s"), sprint_hex(words[EM4X50_DEVICE_SERIAL].byte, 4)); + return res; +} + +int em4x50_read(em4x50_data_t *etd, em4x50_word_t *out) { + + // envoke reading + // - with given address (option b) (and optional password if address is + // read protected) -> selective read mode + + em4x50_data_t edata = { .pwd_given = false, .addr_given = false }; + + if (etd != NULL) { + edata = *etd; + } + + clearCommandBuffer(); + SendCommandNG(CMD_LF_EM4X50_READ, (uint8_t *)&edata, sizeof(edata)); + + PacketResponseNG resp; + if (!WaitForResponseTimeout(CMD_LF_EM4X50_READ, &resp, TIMEOUT)) { + PrintAndLogEx(WARNING, "(em4x50) timeout while waiting for reply."); + return PM3_ETIMEOUT; + } + + if (resp.status != PM3_SUCCESS) + return PM3_ESOFT; + + uint8_t *data = resp.data.asBytes; + em4x50_word_t words[EM4X50_NO_WORDS]; + prepare_result(data, etd->addresses & 0xFF, (etd->addresses >> 8) & 0xFF, words); + + if (out != NULL) + memcpy(out, &words, sizeof(em4x50_word_t) * EM4X50_NO_WORDS); + + print_result(words, etd->addresses & 0xFF, (etd->addresses >> 8) & 0xFF); + + return PM3_SUCCESS; +} + +int CmdEM4x50Read(const char *Cmd) { + + int pwdLen = 0; + int addr = 0; + uint8_t pwd[4] = {0x0}; + em4x50_data_t etd; + + // init + memset(&etd, 0x00, sizeof(em4x50_data_t)); + etd.addr_given = false; + etd.pwd_given = false; + + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf em 4x50_read", + "Reads single EM4x50 block/word.", + "lf em 4x50_read -b 3 -> reads block 3\n" + "lf em 4x50_read -b 32 -p 12345678 -> reads block 32 with password 0x12345678\n" + ); + + void *argtable[] = { + arg_param_begin, + arg_int1("b", "block", "
", "block/word address, dec"), + arg_str0("p", "passsword", "", "password, hex, 4 bytes, lsb"), + arg_param_end + }; + + CLIExecWithReturn(ctx, Cmd, argtable, true); + + addr = arg_get_int_def(ctx, 1, 0); + CLIGetHexWithReturn(ctx, 2, pwd, &pwdLen); + + if (addr <= 0 || addr >= EM4X50_NO_WORDS) { + return PM3_EINVARG; + } else { + etd.addresses = (addr << 8) | addr; + etd.addr_given = true; + } + + if (pwdLen) { + if (pwdLen != 4) { + PrintAndLogEx(FAILED, "password length must be 4 bytes instead of %d", pwdLen); + return PM3_EINVARG; + } else { + etd.password1 = (pwd[0] << 24) | (pwd[1] << 16) | (pwd[2] << 8) | pwd[3]; + etd.pwd_given = true; + } + } + + CLIParserFree(ctx); + + return em4x50_read(&etd, NULL); +} + +int CmdEM4x50StdRead(const char *Cmd) { + + int now = 0, status = PM3_EFAILED; + PacketResponseNG resp; + + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf em 4x50_stdread", + "Shows standard read data of EM4x50 tag.", + "lf em 4x50_stdread\n" + ); + + void *argtable[] = { + arg_param_begin, + arg_param_end + }; + + CLIExecWithReturn(ctx, Cmd, argtable, true); + CLIParserFree(ctx); + + // start + clearCommandBuffer(); + SendCommandNG(CMD_LF_EM4X50_STDREAD, 0, 0); + if (!WaitForResponseTimeout(CMD_LF_EM4X50_STDREAD, &resp, TIMEOUT)) { + PrintAndLogEx(WARNING, "Timeout while waiting for reply."); + return PM3_ETIMEOUT; + } + + now = resp.status; + + // print response + if (now > 0) { + + em4x50_word_t words[EM4X50_NO_WORDS]; + + prepare_result(resp.data.asBytes, 0, now - 1, words); + + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, " # | word (msb) | word (lsb) "); + PrintAndLogEx(INFO, "----+-------------+-------------"); + + for (int i = 0; i < now; i++) { + + char r[30] = {0}; + for (int j = 3; j >= 0; j--) + sprintf(r + strlen(r), "%02x ", reflect8(words[i].byte[j])); + + PrintAndLogEx(INFO, " %2i | " _GREEN_("%s") "| %s", + i, + sprint_hex(words[i].byte, 4), + r + ); + } + + status = PM3_SUCCESS; + PrintAndLogEx(INFO, "----+-------------+-------------"); + PrintAndLogEx(SUCCESS, "Standard read " _GREEN_("ok")); + + } else { + PrintAndLogEx(FAILED, "Standard read " _RED_("failed")); + } + + return status; +} + +int CmdEM4x50Info(const char *Cmd) { + + // envoke reading of a EM4x50 tag which has to be on the antenna because + // decoding is done by the device (not on client side) + + int pwdLen = 0; + int status = 0; + uint8_t pwd[4] = {0x0}; + em4x50_data_t etd = {.pwd_given = false}; + + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf em 4x50_info", + "Tag information EM4x50.", + "lf em 4x50_info\n" + "lf em 4x50_info -p 12345678 -> uses password 0x12345678\n" + ); + + void *argtable[] = { + arg_param_begin, + arg_str0("p", "passsword", "", "password, hex, 4 bytes, lsb"), + arg_param_end + }; + + CLIExecWithReturn(ctx, Cmd, argtable, true); + + CLIGetHexWithReturn(ctx, 1, pwd, &pwdLen); + + if (pwdLen) { + if (pwdLen != 4) { + PrintAndLogEx(FAILED, "password length must be 4 bytes instead of %d", pwdLen); + return PM3_EINVARG; + } else { + etd.password1 = (pwd[0] << 24) | (pwd[1] << 16) | (pwd[2] << 8) | pwd[3]; + etd.pwd_given = true; + } + } + + CLIParserFree(ctx); + + clearCommandBuffer(); + SendCommandNG(CMD_LF_EM4X50_INFO, (uint8_t *)&etd, sizeof(etd)); + + PacketResponseNG resp; + if (!WaitForResponseTimeout(CMD_LF_EM4X50_INFO, &resp, TIMEOUT)) { + PrintAndLogEx(WARNING, "Timeout while waiting for reply."); + return PM3_ETIMEOUT; + } + + status = resp.status; + + if (status == PM3_SUCCESS) + print_info_result(resp.data.asBytes); + else + PrintAndLogEx(FAILED, "Reading tag " _RED_("failed")); + + return status; +} + +int CmdEM4x50Watch(const char *Cmd) { + + // continously envoke reading of a EM4x50 tag + + PacketResponseNG resp; + + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf em 4x50_watch", + "Watches for EM4x50 tags. Function runs until button is pressed.", + "lf em 4x50_watch\n" + ); + + void *argtable[] = { + arg_param_begin, + arg_param_end + }; + + CLIExecWithReturn(ctx, Cmd, argtable, true); + CLIParserFree(ctx); + + PrintAndLogEx(SUCCESS, "Watching for EM4x50 cards - place tag on antenna"); + PrintAndLogEx(INFO, "You can cancel this operation by pressing the pm3 button"); + + clearCommandBuffer(); + SendCommandNG(CMD_LF_EM4X50_WATCH, 0, 0); + WaitForResponseTimeoutW(CMD_LF_EM4X50_WATCH, &resp, -1, false); + + PrintAndLogEx(INFO, "Done"); + return PM3_SUCCESS; +} + +int CmdEM4x50Dump(const char *Cmd) { + + int fnLen = 0, pwdLen = 0; + uint8_t pwd[4] = {0x0}; + char filename[FILE_PATH_SIZE] = {0}; + char *fptr = filename; + em4x50_data_t etd = {.pwd_given = false}; + uint8_t data[DUMP_FILESIZE] = {0}; + + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf em 4x50_dump", + "Reads all blocks/words from EM4x50 tag and saves dump in bin/eml/json format.", + "lf em 4x50_dump -> saves dump in lf-4x50--dump.bin/eml/json\n" + "lf em 4x50_dump -f mydump.eml -> saves dump in mydump.eml\n" + "lf em 4x50_dump -p 12345678\n" + "lf em 4x50_dump -f mydump.eml -p 12345678\n" + ); + + void *argtable[] = { + arg_param_begin, + arg_str0("f", "filename", "", "dump filename (bin/eml/json)"), + arg_str0("p", "passsword", "", "password, hex, 4 bytes, lsb"), + arg_param_end + }; + + CLIExecWithReturn(ctx, Cmd, argtable, true); + + CLIParamStrToBuf(arg_get_str(ctx, 1), + (uint8_t *)filename, + FILE_PATH_SIZE, + &fnLen + ); + CLIGetHexWithReturn(ctx, 2, pwd, &pwdLen); + + if (pwdLen) { + if (pwdLen != 4) { + PrintAndLogEx(FAILED, "password length must be 4 bytes instead of %d", pwdLen); + return PM3_EINVARG; + } else { + etd.password1 = (pwd[0] << 24) | (pwd[1] << 16) | (pwd[2] << 8) | pwd[3]; + etd.pwd_given = true; + } + } + + CLIParserFree(ctx); + + PrintAndLogEx(INFO, "Reading EM4x50 tag"); + clearCommandBuffer(); + SendCommandNG(CMD_LF_EM4X50_INFO, (uint8_t *)&etd, sizeof(etd)); + PacketResponseNG resp; + if (!WaitForResponseTimeout(CMD_LF_EM4X50_INFO, &resp, TIMEOUT)) { + PrintAndLogEx(WARNING, "Timeout while waiting for reply."); + return PM3_ETIMEOUT; + } + + bool success = (resp.status & STATUS_SUCCESS) >> 1; + if (success == false) { + PrintAndLogEx(FAILED, "Reading tag " _RED_("failed")); + return PM3_ESOFT; + } + + // structured format + em4x50_word_t words[EM4X50_NO_WORDS]; + prepare_result(resp.data.asBytes, 0, EM4X50_NO_WORDS - 1, words); + + // result output + PrintAndLogEx(INFO, _YELLOW_("EM4x50 data:")); + print_result(words, 0, EM4X50_NO_WORDS - 1); + + // user supplied filename? + if (fnLen == 0) { + PrintAndLogEx(INFO, "Using UID as filename"); + fptr += sprintf(fptr, "lf-4x50-"); + FillFileNameByUID(fptr, words[EM4X50_DEVICE_ID].byte, "-dump", 4); + } + + for (int i = 0; i < EM4X50_NO_WORDS; i++) + memcpy(data + (i * 4), words[i].byte, 4); + + // saveFileEML will add .eml extension to filename + // saveFile (binary) passes in the .bin extension. + // saveFileJSON adds .json extension + saveFileEML(filename, data, sizeof(data), 4); + saveFile(filename, ".bin", data, sizeof(data)); + saveFileJSON(filename, jsfEM4x50, data, sizeof(data), NULL); + + return PM3_SUCCESS; +} + +//============================================================================== +// write functions +//============================================================================== + +int CmdEM4x50Write(const char *Cmd) { + + // envoke writing a single word (32 bit) to a EM4x50 tag + + int wordLen = 0, pwdLen = 0, addr = 0; + int status = 0; + uint8_t word[4] = {0x0}; + uint8_t pwd[4] = {0x0}; + em4x50_data_t etd = {.pwd_given = false}; + + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf em 4x50_write", + "Writes single block/word to EM4x50 tag.", + "lf em 4x50_write -b 3 -d 4f22e7ff -> writes 0x4f22e7ff to block 3\n" + "lf em 4x50_write -b 3 -d 4f22e7ff -p 12345678\n" + ); + + void *argtable[] = { + arg_param_begin, + arg_int1("b", "block", "
", "block/word address, dec"), + arg_str1("d", "data", "", "data, hex, 4 bytes, lsb"), + arg_str0("p", "passsword", "", "password, hex, 4 bytes, lsb"), + arg_param_end + }; + + CLIExecWithReturn(ctx, Cmd, argtable, true); + + addr = arg_get_int_def(ctx, 1, 0); + CLIGetHexWithReturn(ctx, 2, word, &wordLen); + CLIGetHexWithReturn(ctx, 3, pwd, &pwdLen); + + if (addr <= 0 || addr >= EM4X50_NO_WORDS) { + PrintAndLogEx(FAILED, "address has to be within range [0, 31]"); + return PM3_EINVARG; + } else { + etd.addresses = (addr << 8) | addr; + etd.addr_given = true; + } + if (wordLen != 4) { + PrintAndLogEx(FAILED, "word/data length must be 4 bytes instead of %d", wordLen); + return PM3_EINVARG; + } else { + etd.word = (word[0] << 24) | (word[1] << 16) | (word[2] << 8) | word[3]; + } + if (pwdLen) { + if (pwdLen != 4) { + PrintAndLogEx(FAILED, "password length must be 4 bytes instead of %d", pwdLen); + return PM3_EINVARG; + } else { + etd.password1 = (pwd[0] << 24) | (pwd[1] << 16) | (pwd[2] << 8) | pwd[3]; + etd.pwd_given = true; + } + } + + CLIParserFree(ctx); + + clearCommandBuffer(); + SendCommandNG(CMD_LF_EM4X50_WRITE, (uint8_t *)&etd, sizeof(etd)); + PacketResponseNG resp; + if (!WaitForResponseTimeout(CMD_LF_EM4X50_WRITE, &resp, TIMEOUT)) { + PrintAndLogEx(WARNING, "Timeout while waiting for reply."); + return PM3_ETIMEOUT; + } + + status = resp.status; + if (status == PM3_ETEAROFF) + return PM3_SUCCESS; + + if (status != PM3_SUCCESS) { + PrintAndLogEx(FAILED, "Writing " _RED_("failed")); + return PM3_ESOFT; + } + + // display result of writing operation in structured format + uint8_t *data = resp.data.asBytes; + em4x50_word_t words[EM4X50_NO_WORDS]; + + prepare_result(data, addr, addr, words); + print_result(words, addr, addr); + PrintAndLogEx(SUCCESS, "Successfully wrote to tag"); + PrintAndLogEx(HINT, "Try `" _YELLOW_("lf em 4x50_read a %u") "` - to read your data", addr); + + return PM3_SUCCESS; +} + +int CmdEM4x50WritePwd(const char *Cmd) { + + // envokes changing the password of EM4x50 tag + + int status = PM3_EFAILED; + int pwdLen = 0, npwdLen = 0; + uint8_t pwd[4] = {0x0}, npwd[4] = {0x0}; + PacketResponseNG resp; + em4x50_data_t etd; + + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf em 4x50_writepwd", + "Writes EM4x50 password.", + "lf em 4x50_writepwd -p 4f22e7ff -n 12345678 -> replaces password 0x4f22e7ff with 0x12345678\n" + ); + + void *argtable[] = { + arg_param_begin, + arg_str1("p", "pwd", "", "password, hex, 4 bytes, lsb"), + arg_str1("n", "newpwd", "", "new password, hex, 4 bytes, lsb"), + arg_param_end + }; + + CLIExecWithReturn(ctx, Cmd, argtable, true); + + CLIGetHexWithReturn(ctx, 1, pwd, &pwdLen); + CLIGetHexWithReturn(ctx, 2, npwd, &npwdLen); + + if (pwdLen != 4) { + PrintAndLogEx(FAILED, "password length must be 4 bytes instead of %d", pwdLen); + return PM3_EINVARG; + } else { + etd.password1 = (pwd[0] << 24) | (pwd[1] << 16) | (pwd[2] << 8) | pwd[3]; + } + if (npwdLen != 4) { + PrintAndLogEx(FAILED, "password length must be 4 bytes instead of %d", npwdLen); + return PM3_EINVARG; + } else { + etd.password2 = (npwd[0] << 24) | (npwd[1] << 16) | (npwd[2] << 8) | npwd[3]; + } + + CLIParserFree(ctx); + + clearCommandBuffer(); + SendCommandNG(CMD_LF_EM4X50_WRITEPWD, (uint8_t *)&etd, sizeof(etd)); + + if (!WaitForResponseTimeout(CMD_LF_EM4X50_WRITEPWD, &resp, TIMEOUT)) { + PrintAndLogEx(WARNING, "Timeout while waiting for reply."); + return PM3_ETIMEOUT; + } + + status = resp.status; + + if (status == PM3_ETEAROFF) + return PM3_SUCCESS; + + // print response + if (status != PM3_SUCCESS) { + PrintAndLogEx(FAILED, "Writing password " _RED_("failed")); + return PM3_EFAILED; + } + + PrintAndLogEx(SUCCESS, "Writing new password " _GREEN_("ok")); + + return PM3_SUCCESS; +} + +int CmdEM4x50Wipe(const char *Cmd) { + + // fills EM4x50 tag with zeros including password + + bool isOK = false; + int pwdLen = 0; + uint8_t pwd[4] = {0x0}; + em4x50_data_t etd = {.pwd_given = false, .word = 0x0, .password2 = 0x0}; + PacketResponseNG resp; + + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf em 4x50_wipe", + "Wipes EM4x50 tag.", + "lf em 4x50_wipe -p 12345678 -> wipes tag with password 0x12345678\n" + ); + + void *argtable[] = { + arg_param_begin, + arg_str1("p", "passsword", "", "password, hex, 4 bytes, lsb"), + arg_param_end + }; + + CLIExecWithReturn(ctx, Cmd, argtable, true); + + CLIGetHexWithReturn(ctx, 1, pwd, &pwdLen); + if (pwdLen != 4) { + PrintAndLogEx(FAILED, "password length must be 4 bytes instead of %d", pwdLen); + return PM3_EINVARG; + } else { + etd.password1 = (pwd[0] << 24) | (pwd[1] << 16) | (pwd[2] << 8) | pwd[3]; + etd.pwd_given = true; + } + + CLIParserFree(ctx); + + // clear password + clearCommandBuffer(); + SendCommandNG(CMD_LF_EM4X50_WRITEPWD, (uint8_t *)&etd, sizeof(etd)); + if (!WaitForResponseTimeout(CMD_LF_EM4X50_WRITEPWD, &resp, TIMEOUT)) { + PrintAndLogEx(WARNING, "Timeout while waiting for reply."); + return PM3_ETIMEOUT; + } + + if (resp.status == PM3_SUCCESS) { + PrintAndLogEx(SUCCESS, "Resetting password " _GREEN_("ok")); + } else { + PrintAndLogEx(FAILED, "Resetting password " _RED_("failed")); + return PM3_ESOFT; + } + + // from now on new password 0x0 + etd.password1 = 0x0; + + // clear data (words 1 to 31) + for (int i = 1; i < EM4X50_DEVICE_SERIAL; i++) { + + // no login necessary for blocks 3 to 31 + etd.pwd_given = (i <= EM4X50_CONTROL); + + PrintAndLogEx(INPLACE, "Wiping block %i", i); + + etd.addresses = i << 8 | i; + clearCommandBuffer(); + SendCommandNG(CMD_LF_EM4X50_WRITE, (uint8_t *)&etd, sizeof(etd)); + if (!WaitForResponseTimeout(CMD_LF_EM4X50_WRITE, &resp, TIMEOUT)) { + PrintAndLogEx(WARNING, "Timeout while waiting for reply."); + return PM3_ETIMEOUT; + } + + isOK = resp.status; + if (!isOK) { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(FAILED, "Wiping data " _RED_("failed")); + return PM3_ESOFT; + } + } + + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(SUCCESS, "Wiping data " _GREEN_("ok")); + + PrintAndLogEx(INFO, "Done"); + + return PM3_SUCCESS; +} + +int CmdEM4x50Restore(const char *Cmd) { + + int uidLen = 0, fnLen = 0, pwdLen = 0, status = 0; + uint8_t pwd[4] = {0x0}, uid[4] = {0x0}; + size_t bytes_read = 0; + char filename[FILE_PATH_SIZE] = {0}; + em4x50_data_t etd = {.pwd_given = false}; + uint8_t data[DUMP_FILESIZE] = {0x0}; + + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf em 4x50_restore", + "Restores data from dumpfile onto a Em4x50 tag.", + "lf em 4x50_restore -u 1b5aff5c -> uses lf-4x50-1B5AFF5C-dump.bin\n" + "lf em 4x50_restore -f mydump.eml -> uses mydump.eml\n" + "lf em 4x50_restore -u 1b5aff5c -p 12345678 -> \n" + "lf em 4x50_restore -f mydump.eml -p 12345678 -> \n" + ); + + void *argtable[] = { + arg_param_begin, + arg_str0("u", "uid", "", "uid, hex, 4 bytes, msb, restore from lf-4x50--dump.bin"), + arg_str0("f", "filename", "", "dump filename (bin/eml/json)"), + arg_str0("p", "passsword", "", "password, hex, 4 bytes, lsb"), + arg_param_end + }; + + CLIExecWithReturn(ctx, Cmd, argtable, true); + + CLIGetHexWithReturn(ctx, 1, uid, &uidLen); + CLIParamStrToBuf(arg_get_str(ctx, 2), + (uint8_t *)filename, + FILE_PATH_SIZE, + &fnLen + ); + CLIGetHexWithReturn(ctx, 3, pwd, &pwdLen); + + if ((uidLen && fnLen) || (!uidLen && !fnLen)) { + PrintAndLogEx(FAILED, "either use option 'u' or option 'f'"); + return PM3_EINVARG; + } + + if (uidLen) { + snprintf(filename, FILE_PATH_SIZE, "./lf-4x50-%02x%02x%02x%02x-dump.bin", + uid[0], + uid[1], + uid[2], + uid[3] + ); + } + + if (pwdLen) { + if (pwdLen != 4) { + PrintAndLogEx(FAILED, "password length must be 4 bytes instead of %d", pwdLen); + return PM3_EINVARG; + } else { + etd.password1 = (pwd[0] << 24) | (pwd[1] << 16) | (pwd[2] << 8) | pwd[3]; + etd.pwd_given = true; + } + } + + CLIParserFree(ctx); + + PrintAndLogEx(INFO, "Restoring " _YELLOW_("%s")" to card", filename); + + // read data from dump file; file type has to be "bin", "eml" or "json" + if (em4x50_load_file(filename, data, DUMP_FILESIZE, &bytes_read) != PM3_SUCCESS) + return PM3_EFILE; + + // upload to flash memory + status = em4x50_write_flash(data, 0, bytes_read); + if (status != PM3_SUCCESS) { + PrintAndLogEx(WARNING, "Error uploading to flash."); + return status; + } + + clearCommandBuffer(); + SendCommandNG(CMD_LF_EM4X50_RESTORE, (uint8_t *)&etd, sizeof(etd)); + PacketResponseNG resp; + if (!WaitForResponseTimeout(CMD_LF_EM4X50_RESTORE, &resp, 2 * TIMEOUT)) { + PrintAndLogEx(FAILED, "Timeout while waiting for reply."); + return PM3_ETIMEOUT; + } + + status = resp.status; + + if (status == PM3_ETEAROFF) { + return PM3_SUCCESS; + } else if (status != PM3_SUCCESS) { + PrintAndLogEx(FAILED, "Restore " _RED_("failed")); + return status; + } + + PrintAndLogEx(SUCCESS, "Restore " _GREEN_("ok")); + PrintAndLogEx(INFO, "Finished restoring"); + + return PM3_SUCCESS; +} + +//============================================================================== +// simulate functions +//============================================================================== + +int CmdEM4x50Sim(const char *Cmd) { + + int slen = 0, res = 0; + size_t bytes_read = 0; + uint8_t data[DUMP_FILESIZE] = {0x0}; + char filename[FILE_PATH_SIZE] = {0}; + PacketResponseNG resp; + + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf em 4x50_sim", + "Simulates a EM4x50 tag", + "lf em 4x50_sim -> simulates EM4x50 data in flash memory.\n" + "lf em 4x50_sim -f mydump.eml -> simulates content of file ./mydump\n" + ); + + void *argtable[] = { + arg_param_begin, + arg_str0("f", "filename", "", "dump filename, bin/eml/json"), + arg_param_end + }; + + CLIExecWithReturn(ctx, Cmd, argtable, true); + CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &slen); + CLIParserFree(ctx); + + // read data from dump file; file type has to be "bin", "eml" or "json" + if (slen != 0) { + + // load file content + if (em4x50_load_file(filename, data, DUMP_FILESIZE, &bytes_read) != PM3_SUCCESS) { + PrintAndLogEx(FAILED, "Read error"); + return PM3_EFILE; + } + + if (bytes_read * 8 > FLASH_MEM_MAX_SIZE) { + PrintAndLogEx(FAILED, "Filesize is larger than available memory"); + return PM3_EOVFLOW; + } + + PrintAndLogEx(INFO, "Uploading dump " _YELLOW_("%s") " to flash memory", filename); + + if (res != PM3_SUCCESS) { + PrintAndLogEx(WARNING, "Error wiping flash."); + return res; + } + + // upload to device + res = em4x50_write_flash(data, 0, bytes_read); + if (res != PM3_SUCCESS) { + PrintAndLogEx(WARNING, "Error uploading to flash."); + return res; + } + } + + PrintAndLogEx(INFO, "Simulating data in " _YELLOW_("%s"), filename); + + clearCommandBuffer(); + SendCommandNG(CMD_LF_EM4X50_SIM, 0, 0); + + WaitForResponse(CMD_LF_EM4X50_SIM, &resp); + + if (resp.status == PM3_ETEAROFF) { + return PM3_SUCCESS; + } else if (resp.status == PM3_ENODATA) { + PrintAndLogEx(FAILED, "No valid em4x50 data in flash memory."); + return PM3_ENODATA; + } + + PrintAndLogEx(INFO, "Done"); + return PM3_SUCCESS; +}