diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a2c3e0ad..323774a34 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ All notable changes to this project will be documented in this file. This project uses the changelog in accordance with [keepchangelog](http://keepachangelog.com/). Please use this to write notable changes, which is not the same as git commit log... ## [unreleased][unreleased] + - Added support for bidirectional communication for `lf em 4x50 sim` (@tharexde) - Added `tools/hitag2crack/crack5opencl`, an optimized version of `crack5gpu` (@matrix) - Fixed Makefile to account for changes when running on Apple Silicon (@tcprst) - Added support for debugging ARM with JTAG & VSCode (@Gator96100) diff --git a/armsrc/appmain.c b/armsrc/appmain.c index 480a8632b..dca2def3b 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -1145,7 +1145,7 @@ static void PacketReceived(PacketCommandNG *packet) { // destroy the Emulator Memory. //----------------------------------------------------------------------------- FpgaDownloadAndGo(FPGA_BITSTREAM_LF); - em4x50_sim((uint8_t *)packet->data.asBytes); + em4x50_sim((uint32_t *)packet->data.asBytes); break; } case CMD_LF_EM4X50_READER: { diff --git a/armsrc/em4x50.c b/armsrc/em4x50.c index 437afd956..f27a21367 100644 --- a/armsrc/em4x50.c +++ b/armsrc/em4x50.c @@ -11,6 +11,7 @@ #include "fpgaloader.h" #include "ticks.h" #include "dbprint.h" +#include "lfsampling.h" #include "lfadc.h" #include "lfdemod.h" #include "commonutil.h" @@ -34,16 +35,22 @@ #define EM4X50_T_TAG_FULL_PERIOD 64 #define EM4X50_T_TAG_TPP 64 #define EM4X50_T_TAG_TWA 64 +#define EM4X50_T_TAG_TINIT 2112 +#define EM4X50_T_TAG_TWEE 3200 #define EM4X50_T_TAG_WAITING_FOR_SIGNAL 75 #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 +#define EM4X50_T_WAITING_FOR_ACK 4 + +// timeout values for simulation mode (may vary with regard to reader) +#define EM4X50_T_SIMULATION_TIMEOUT_READ 600 +#define EM4X50_T_SIMULATION_TIMEOUT_WAIT 50 + +// the following value seems to be critical; if it's too low (e.g. < 120) +// some cards are no longer readable although they're ok +#define EM4X50_T_WAITING_FOR_SNGLLIW 140 #define EM4X50_TAG_TOLERANCE 8 +#define EM4X50_ZERO_DETECTION 3 #define EM4X50_TAG_WORD 45 #define EM4X50_TAG_MAX_NO_BYTES 136 @@ -52,10 +59,19 @@ #define EM4X50_COMMAND_WRITE 0x12 #define EM4X50_COMMAND_WRITE_PASSWORD 0x11 #define EM4X50_COMMAND_SELECTIVE_READ 0x0A +#define EM4X50_COMMAND_STANDARD_READ 0x02 // virtual command int gHigh = 190; int gLow = 60; +// indication whether a previous login has been successful, so operations +// that require authentication can be handled +bool gLogin = false; +// WritePassword process in simulation mode is handled in a different way +// compared to operations like read, write, login, so it is necessary to +// to be able to identfiy it +bool gWritePasswordProcess = false; + // do nothing for using timer0 static void wait_timer(uint32_t period) { AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG; @@ -169,6 +185,17 @@ static void em4x50_setup_sim(void) { AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DOUT | GPIO_SSC_CLK; AT91C_BASE_PIOA->PIO_OER = GPIO_SSC_DOUT; AT91C_BASE_PIOA->PIO_ODR = GPIO_SSC_CLK; + + AT91C_BASE_PMC->PMC_PCER |= (1 << AT91C_ID_TC0); + AT91C_BASE_PIOA->PIO_BSR = GPIO_SSC_FRAME; + AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; + AT91C_BASE_TC0->TC_CMR = AT91C_TC_CLKS_TIMER_DIV1_CLOCK; + AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; + + // Watchdog hit + WDT_HIT(); + + LEDsoff(); } // calculate signal properties (mean amplitudes) from measured data: @@ -301,12 +328,12 @@ static void em4x50_reader_send_bit(int bit) { if (bit == 0) { - // disable modulation (drops the field) for 7 cycles of carrier + // disable modulation (activate the field) for 7 cycles of carrier // period (Opt64) LOW(GPIO_SSC_DOUT); while (AT91C_BASE_TC0->TC_CV < T0 * 7); - // enable modulation (activates the field) for remaining first + // enable modulation (drop the field) for remaining first // half of bit period HIGH(GPIO_SSC_DOUT); while (AT91C_BASE_TC0->TC_CV < T0 * EM4X50_T_TAG_HALF_PERIOD); @@ -330,7 +357,7 @@ static void em4x50_reader_send_byte(uint8_t byte) { } } -// send byte followed by its (equal) parity bit +// send byte followed by its (even) parity bit static void em4x50_reader_send_byte_with_parity(uint8_t byte) { int parity = 0, bit = 0; @@ -411,8 +438,6 @@ static int find_double_listen_window(bool bcommand) { if (bcommand) { -// SpinDelay(10); - // data transmission from card has to be stopped, because // a commamd shall be issued @@ -476,9 +501,8 @@ static int request_receive_mode(void) { // If is true then within the single listen window right after the // ack signal a RM request has to be sent. static bool check_ack(bool bliw) { - AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG; - while (AT91C_BASE_TC0->TC_CV < T0 * 4 * EM4X50_T_TAG_FULL_PERIOD) { - + int count_cycles = 0; + while (count_cycles < EM4X50_T_WAITING_FOR_ACK) { if (BUTTON_PRESS()) return false; @@ -519,6 +543,7 @@ static bool check_ack(bool bliw) { break; } } + count_cycles++; } return false; @@ -619,154 +644,6 @@ static int get_word_from_bitstream(uint32_t *data) { return PM3_EOPABORTED; } -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) == false) - return false;; - - if (em4x50_sim_send_bit(parity) == false) - return false; - - return true; -} - -static bool em4x50_sim_send_word(uint32_t word) { - - uint8_t cparity = 0x00; - - // word has tobe sent in msb, not lsb - word = reflect32(word); - - // 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) == false) { - 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) == false) - return false; - - // stop bit - if (em4x50_sim_send_bit(0) == false) - return false; - - return true; -} - -static bool em4x50_sim_send_listen_window(void) { - - uint16_t check = 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; -} - // simple login to EM4x50, // used in operations that require authentication static bool login(uint32_t password) { @@ -1085,8 +962,12 @@ static int write(uint32_t word, uint32_t addresses) { // look for ACK sequence if (check_ack(false)) { - // now EM4x50 needs T0 * EM4X50_T_TAG_TWEE (EEPROM write time) + // now EM4x50 needs T0 * EM4X50_T_TAG_TWEE (EEPROM write time = 3.2ms = 50 * 64 periods) // for saving data and should return with ACK + for (int i = 0; i < 50; i++) { + wait_timer(T0 * EM4X50_T_TAG_FULL_PERIOD); + } + if (check_ack(false)) return PM3_SUCCESS; } @@ -1127,9 +1008,17 @@ static int write_password(uint32_t password, uint32_t new_password) { // wait for T0 * EM4X50_T_TAG_TWA (write access time) wait_timer(T0 * EM4X50_T_TAG_TWA); - if (check_ack(false)) + if (check_ack(false)) { + + // now EM4x50 needs T0 * EM4X50_T_TAG_TWEE (EEPROM write time = 3.2ms = 50 * 64 periods) + // for saving data and should return with ACK + for (int i = 0; i < 50; i++) { + wait_timer(T0 * EM4X50_T_TAG_FULL_PERIOD); + } + if (check_ack(false)) return PM3_SUCCESS; + } } } } else { @@ -1218,66 +1107,719 @@ void em4x50_writepwd(em4x50_data_t *etd) { reply_ng(CMD_LF_EM4X50_WRITEPWD, status, NULL, 0); } -// simulate uploaded data in emulator memory -// (currently simulation allows only a one-way communication) -void em4x50_sim(uint8_t *filename) { - int status = PM3_SUCCESS; - uint8_t *em4x50_mem = BigBuf_get_EM_addr(); - uint32_t words[EM4X50_NO_WORDS] = {0x0}; +// send bit in receive mode by counting carrier cycles +static void em4x50_sim_send_bit(uint8_t bit) { -#ifdef WITH_FLASH + //uint16_t timeout = EM4X50_T_TAG_FULL_PERIOD; + uint16_t timeout = EM4X50_T_SIMULATION_TIMEOUT_READ; - if (strlen((char *)filename) != 0) { + for (int t = 0; t < EM4X50_T_TAG_FULL_PERIOD; t++) { - BigBuf_free(); + // wait until SSC_CLK goes HIGH + // used as a simple detection of a reader field? + while ((timeout--) && !(AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_CLK)); + + if (timeout <= 0) { + return; + } + timeout = EM4X50_T_SIMULATION_TIMEOUT_READ; - int changed = rdv40_spiffs_lazy_mount(); - uint32_t size = size_in_spiffs((char *)filename); - em4x50_mem = BigBuf_malloc(size); + if (bit) + OPEN_COIL(); + else + SHORT_COIL(); - rdv40_spiffs_read_as_filetype((char *)filename, em4x50_mem, size, RDV40_SPIFFS_SAFETY_SAFE); + //wait until SSC_CLK goes LOW + while ((timeout--) && (AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_CLK)); + if (timeout <= 0) { + return; + } + timeout = EM4X50_T_SIMULATION_TIMEOUT_READ; - if (changed) - rdv40_spiffs_lazy_unmount(); + if (t == EM4X50_T_TAG_HALF_PERIOD) + bit ^= 1; + + } +} + +// send byte in receive mode either with or without parity check (even) +static void em4x50_sim_send_byte(uint8_t byte, bool paritycheck) { + + // send byte + for (int i = 0; i < 8; i++) { + em4x50_sim_send_bit((byte >> (7 - i)) & 1); } -#endif + if (paritycheck) { - for (int i = 0; i < EM4X50_NO_WORDS; i++) - words[i] = reflect32(bytes_to_num(em4x50_mem + (i * 4), 4)); + uint8_t parity = 0x0; - // only if valid em4x50 data (e.g. uid == serial) - if (words[EM4X50_DEVICE_SERIAL] != words[EM4X50_DEVICE_ID]) { + for (int i = 0; i < 8; i++) { + parity ^= (byte >> i) & 1; + } - // extract control data - int fwr = words[CONFIG_BLOCK] & 0xFF; // first word read - int lwr = (words[CONFIG_BLOCK] >> 8) & 0xFF; // last word read - // extract protection data - int fwrp = words[EM4X50_PROTECTION] & 0xFF; // first word read protected - int lwrp = (words[EM4X50_PROTECTION] >> 8) & 0xFF; // last word read protected + em4x50_sim_send_bit(parity); + } +} - em4x50_setup_sim(); +// send complete word in receive mode (including all parity checks) +static void em4x50_sim_send_word(uint32_t word) { - // iceman, will need a usb cmd check to break as well - while (BUTTON_PRESS() == false) { + uint8_t cparity = 0x00; - WDT_HIT(); - em4x50_sim_send_listen_window(); - for (int i = fwr; i <= lwr; i++) { + // word has tobe sent in msb, not lsb + word = reflect32(word); - em4x50_sim_send_listen_window(); + // 4 bytes each with even row parity bit + for (int i = 0; i < 4; i++) { + em4x50_sim_send_byte((word >> ((3 - i) * 8)) & 0xFF, true); + } - if ((i >= fwrp) && (i <= lwrp)) - em4x50_sim_send_word(0x00); - else - em4x50_sim_send_word(words[i]); + // 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; + } + } + em4x50_sim_send_byte(cparity, false); + + // stop bit + em4x50_sim_send_bit(0); +} + +// wait for pulses of carrier frequency +static void wait_cycles(int maxperiods) { + + int period = 0, timeout = EM4X50_T_SIMULATION_TIMEOUT_WAIT; + + while (period < maxperiods) { + + while ((timeout--) && !(AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_CLK)); + if (timeout <= 0) { + return; + } + timeout = EM4X50_T_SIMULATION_TIMEOUT_WAIT; + + while ((timeout--) && (AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_CLK)); + if (timeout <= 0) { + return; + } + timeout = EM4X50_T_SIMULATION_TIMEOUT_WAIT; + + period++; + } +} + +// read single bit in simulation mode +static int em4x50_sim_read_bit(void) { + + int cycles = 0; + int timeout = EM4X50_T_SIMULATION_TIMEOUT_READ; + + while (cycles < EM4X50_T_TAG_FULL_PERIOD) { + + // wait until reader field disappears + while (!(AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_CLK)); + + AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG; + + // now check until reader switches on carrier field + while (AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_CLK) { + + // check if cycle (i.e. off -> on -> off) takes longer than T0 + if (AT91C_BASE_TC0->TC_CV > T0 * EM4X50_ZERO_DETECTION) { + + // gap detected; wait until reader field is switched on again + while ((timeout--) && (AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_CLK)); + + if (timeout <= 0) { + return PM3_ETIMEOUT; + } + timeout = EM4X50_T_SIMULATION_TIMEOUT_READ; + + // now we have a reference "position", from here it will take + // slightly less than 32 cycles until the end of the bit period + wait_cycles(28); + + // end of bit period is reached; return with bit value "0" + // (cf. datasheet) + return 0; } } + + // no gap detected, i.e. reader field is still up; + // continue with counting cycles + cycles++; + } + + // reached 64 cycles (= EM4X50_T_TAG_FULL_PERIOD) -> return bit value "1" + return 1; +} + +// read byte in simulation mode either with or without parity check (even) +static bool em4x50_sim_read_byte(uint8_t *byte, bool paritycheck) { + + for (int i = 0; i < 8; i++) { + *byte <<= 1; + *byte |= em4x50_sim_read_bit(); + } + + if (paritycheck) { + + int pval = em4x50_sim_read_bit(); + uint8_t parity = 0; + + for (int i = 0; i < 8; i++) { + parity ^= ((*byte) >> i) & 1; + } + + if (parity != pval) { + return false; + } + } + + return true; + +} + +// read complete word in simulation mode +static bool em4x50_sim_read_word(uint32_t *word) { + + uint8_t stop_bit = 0; + uint8_t parities = 0, parities_calculated = 0; + uint8_t bytes[4] = {0}; + + // read plain data + for (int i = 0; i < 4; i++) { + em4x50_sim_read_byte(&bytes[i], true); + } + + // read column parities and stop bit + em4x50_sim_read_byte(&parities, false); + stop_bit = em4x50_sim_read_bit(); + + // calculate column parities from data + for (int i = 0; i < 8; i++) { + parities_calculated <<= 1; + for (int j = 0; j < 4; j++) { + parities_calculated ^= (bytes[j] >> (7 - i)) & 1; + } + } + + *word = BYTES2UINT32(bytes); + + // check parities + if ((parities == parities_calculated) && (stop_bit == 0)) { + return true; + } + + return false; +} + +// check if reader requests receive mode (rm) by sending two zeros +static int check_rm_request(uint32_t *tag) { + + // look for first zero + int bit = em4x50_sim_read_bit(); + if (bit == 0) { + + // look for second zero + bit = em4x50_sim_read_bit(); + if (bit == 0) { + + // if command before was EM4X50_COMMAND_WRITE_PASSWORD + // switch to separate process + if (gWritePasswordProcess) { + return EM4X50_COMMAND_WRITE_PASSWORD; + } else { + // read mode request detected, get command from reader + uint8_t command = 0; + em4x50_sim_read_byte(&command, true); + return command; + } + } + } + + return (bit != PM3_ETIMEOUT) ? PM3_SUCCESS : PM3_ETIMEOUT; +} + +// send single listen window in simulation mode +static int em4x50_sim_send_listen_window(uint32_t *tag) { + + SHORT_COIL(); + wait_cycles(EM4X50_T_TAG_HALF_PERIOD); + + OPEN_COIL(); + wait_cycles(EM4X50_T_TAG_HALF_PERIOD); + + SHORT_COIL(); + wait_cycles(2 * EM4X50_T_TAG_FULL_PERIOD); + + OPEN_COIL(); + int command = check_rm_request(tag); + if (command != PM3_SUCCESS) { + return command; + } + + SHORT_COIL(); + wait_cycles(EM4X50_T_TAG_FULL_PERIOD); + + return PM3_SUCCESS; +} + +// send ack +static void em4x50_sim_send_ack(void) { + + SHORT_COIL(); + wait_cycles(EM4X50_T_TAG_HALF_PERIOD); + + OPEN_COIL(); + wait_cycles(EM4X50_T_TAG_HALF_PERIOD); + + SHORT_COIL(); + wait_cycles(3 * EM4X50_T_TAG_HALF_PERIOD); + + OPEN_COIL(); + wait_cycles(EM4X50_T_TAG_HALF_PERIOD); + + SHORT_COIL(); + wait_cycles(3 * EM4X50_T_TAG_HALF_PERIOD); + + OPEN_COIL(); + wait_cycles(EM4X50_T_TAG_HALF_PERIOD); + + SHORT_COIL(); +} + +// send nak +static void em4x50_sim_send_nak(void) { + + SHORT_COIL(); + wait_cycles(EM4X50_T_TAG_HALF_PERIOD); + + OPEN_COIL(); + wait_cycles(EM4X50_T_TAG_HALF_PERIOD); + + SHORT_COIL(); + wait_cycles(3 * EM4X50_T_TAG_HALF_PERIOD); + + OPEN_COIL(); + wait_cycles(EM4X50_T_TAG_HALF_PERIOD); + + SHORT_COIL(); + wait_cycles(EM4X50_T_TAG_FULL_PERIOD); + + OPEN_COIL(); + wait_cycles(EM4X50_T_TAG_HALF_PERIOD); + + SHORT_COIL(); + wait_cycles(EM4X50_T_TAG_HALF_PERIOD); +} + +// standard read mode process (simulation mode) +static int em4x50_sim_handle_standard_read_command(uint32_t *tag) { + + int command = 0; + + // extract control data + int fwr = reflect32(tag[EM4X50_CONTROL]) & 0xFF; // first word read + int lwr = (reflect32(tag[EM4X50_CONTROL]) >> 8) & 0xFF; // last word read + // extract protection data: + // first word read protected + int fwrp = reflect32(tag[EM4X50_PROTECTION]) & 0xFF; + // last word read protected + int lwrp = (reflect32(tag[EM4X50_PROTECTION]) >> 8) & 0xFF; + + while ((BUTTON_PRESS() == false) && (data_available() == false)) { + + WDT_HIT(); + + command = em4x50_sim_send_listen_window(tag); + if (command != PM3_SUCCESS) { + return command; + } + + for (int i = fwr; i <= lwr; i++) { + + command = em4x50_sim_send_listen_window(tag); + if (command != PM3_SUCCESS) { + return command; + } + + if ((gLogin == false) && (i >= fwrp) && (i <= lwrp)) { + em4x50_sim_send_word(0x00); + } else { + em4x50_sim_send_word(reflect32(tag[i])); + } + } + } + + return PM3_EOPABORTED; +} + +// selective read mode process (simulation mode) +static int em4x50_sim_handle_selective_read_command(uint32_t *tag) { + + int command = 0; + + // read password + uint32_t address = 0; + bool addr = em4x50_sim_read_word(&address); + + // processing pause time (corresponds to a "1" bit) + em4x50_sim_send_bit(1); + + if (addr) { + em4x50_sim_send_ack(); } else { - status = PM3_ENODATA; + em4x50_sim_send_nak(); + return EM4X50_COMMAND_STANDARD_READ; + } + + // extract control data + int fwr = address & 0xFF; // first word read + int lwr = (address >> 8) & 0xFF; // last word read + + // extract protection data: + // first word read protected + int fwrp = reflect32(tag[EM4X50_PROTECTION]) & 0xFF; + // last word read protected + int lwrp = (reflect32(tag[EM4X50_PROTECTION]) >> 8) & 0xFF; + + while ((BUTTON_PRESS() == false) && (data_available() == false)) { + + WDT_HIT(); + + command = em4x50_sim_send_listen_window(tag); + if (command != PM3_SUCCESS) { + return command; + } + + for (int i = fwr; i <= lwr; i++) { + + command = em4x50_sim_send_listen_window(tag); + if (command != PM3_SUCCESS) { + return command; + } + + // if not authenticated do not send read protected words + if ((gLogin == false) && (i >= fwrp) && (i <= lwrp)) { + em4x50_sim_send_word(0x00); + } else { + em4x50_sim_send_word(reflect32(tag[i])); + } + } + } + + return PM3_EOPABORTED; +} + +// login process (simulation mode) +static int em4x50_sim_handle_login_command(uint32_t *tag) { + + // read password + uint32_t password = 0; + bool pwd = em4x50_sim_read_word(&password); + + // processing pause time (corresponds to a "1" bit) + em4x50_sim_send_bit(1); + + if (pwd && (password == reflect32(tag[EM4X50_DEVICE_PASSWORD]))) { + em4x50_sim_send_ack(); + gLogin = true; + LED_A_ON(); + } else { + em4x50_sim_send_nak(); + gLogin = false; + LED_A_OFF(); + } + // continue with standard read mode + return EM4X50_COMMAND_STANDARD_READ; +} + +// reset process (simulation mode) +static int em4x50_sim_handle_reset_command(uint32_t *tag) { + + // processing pause time (corresponds to a "1" bit) + em4x50_sim_send_bit(1); + + // send ACK + em4x50_sim_send_ack(); + gLogin = false; + LED_A_OFF(); + + // wait for initialization (tinit) + wait_cycles(EM4X50_T_TAG_TINIT); + + // continue with standard read mode + return EM4X50_COMMAND_STANDARD_READ; +} + +// write process (simulation mode) +static int em4x50_sim_handle_write_command(uint32_t *tag) { + + // read address + uint8_t address = 0; + bool addr = em4x50_sim_read_byte(&address, true); + // read data + uint32_t data = 0; + bool word = em4x50_sim_read_word(&data); + + // write access time + wait_cycles(EM4X50_T_TAG_TWA); + + if ((addr == false) || (word == false)) { + em4x50_sim_send_nak(); + return EM4X50_COMMAND_STANDARD_READ; + } + + // extract necessary control data + bool raw = (tag[EM4X50_CONTROL] >> CONFIG_BLOCK) & READ_AFTER_WRITE; + // extract protection data: + // first word write protected + int fwwp = reflect8((tag[EM4X50_PROTECTION] >> 24) & 0xFF); + // last word write protected + int lwwp = reflect8((tag[EM4X50_PROTECTION] >> 16) & 0xFF); + + switch (address) { + + case EM4X50_DEVICE_PASSWORD: + em4x50_sim_send_nak(); + return EM4X50_COMMAND_STANDARD_READ; + break; + + case EM4X50_PROTECTION: + if (gLogin) { + tag[address] = reflect32(data); + em4x50_sim_send_ack(); + } else { + em4x50_sim_send_nak(); + return EM4X50_COMMAND_STANDARD_READ; + } + break; + + case EM4X50_CONTROL: + if (gLogin) { + tag[address] = reflect32(data); + em4x50_sim_send_ack(); + } else { + em4x50_sim_send_nak(); + return EM4X50_COMMAND_STANDARD_READ; + } + break; + + case EM4X50_DEVICE_SERIAL: + em4x50_sim_send_nak(); + return EM4X50_COMMAND_STANDARD_READ; + break; + + case EM4X50_DEVICE_ID: + em4x50_sim_send_nak(); + return EM4X50_COMMAND_STANDARD_READ; + break; + + default: + if ((address >= fwwp) && (address <= lwwp)) { + if (gLogin) { + tag[address] = reflect32(data); + em4x50_sim_send_ack(); + } else { + em4x50_sim_send_nak(); + return EM4X50_COMMAND_STANDARD_READ; + } + } else { + tag[address] = reflect32(data); + em4x50_sim_send_ack(); + } + break; + } + + // EEPROM write time + // strange: need some sort of 'waveform correction', otherwise ack signal + // will not be detected; sending a single "1" as last "bit" of Twee + // seems to solve the problem + wait_cycles(EM4X50_T_TAG_TWEE - EM4X50_T_TAG_FULL_PERIOD); + em4x50_sim_send_bit(1); + em4x50_sim_send_ack(); + + // if "read after write" (raw) bit is set, send written data once + if (raw) { + int command = em4x50_sim_send_listen_window(tag); + if (command != PM3_SUCCESS) { + return command; + } + + command = em4x50_sim_send_listen_window(tag); + if (command != PM3_SUCCESS) { + return command; + } + + em4x50_sim_send_word(tag[address]); + } + + // continue with standard read mode + return EM4X50_COMMAND_STANDARD_READ; +} + +// write password process (simulation mode) +static int em4x50_sim_handle_writepwd_command(uint32_t *tag) { + + bool pwd = false; + + if (gWritePasswordProcess == false) { + + gWritePasswordProcess = true; + + // read password + uint32_t act_password = 0; + pwd = em4x50_sim_read_word(&act_password); + + // processing pause time (corresponds to a "1" bit) + em4x50_sim_send_bit(1); + + if (pwd && (act_password == reflect32(tag[EM4X50_DEVICE_PASSWORD]))) { + em4x50_sim_send_ack(); + gLogin = true; + } else { + em4x50_sim_send_nak(); + gLogin = false; + return EM4X50_COMMAND_STANDARD_READ; + } + + int command = em4x50_sim_send_listen_window(tag); + if (command != PM3_SUCCESS) { + return command; + } + + } else { + + gWritePasswordProcess = false; + + // read new password + uint32_t new_password = 0; + pwd = em4x50_sim_read_word(&new_password); + + // write access time + wait_cycles(EM4X50_T_TAG_TWA); + + if (pwd) { + em4x50_sim_send_ack(); + tag[EM4X50_DEVICE_PASSWORD] = reflect32(new_password); + } else { + em4x50_sim_send_ack(); + return EM4X50_COMMAND_STANDARD_READ; + } + + // EEPROM write time + // strange: need some sort of 'waveform correction', otherwise ack signal + // will not be detected; sending a single "1" as last part of Twee + // seems to solve the problem + wait_cycles(EM4X50_T_TAG_TWEE - EM4X50_T_TAG_FULL_PERIOD); + em4x50_sim_send_bit(1); + em4x50_sim_send_ack(); + + // continue with standard read mode + return EM4X50_COMMAND_STANDARD_READ; + } + + // call writepwd function again for else branch + return EM4X50_COMMAND_WRITE_PASSWORD; +} + +// simulate uploaded data in emulator memory +// LED A -> operations that require authentication are possible +// LED B -> standard read mode is active +// LED C -> command has been transmitted by reader +void em4x50_sim(uint32_t *password) { + + int command = PM3_ENODATA; + + uint8_t *em4x50_mem = BigBuf_get_EM_addr(); + uint32_t tag[EM4X50_NO_WORDS] = {0x0}; + + for (int i = 0; i < EM4X50_NO_WORDS; i++) + tag[i] = bytes_to_num(em4x50_mem + (i * 4), 4); + + // via eload uploaded dump usually does not contain a password + if (tag[EM4X50_DEVICE_PASSWORD] == 0) { + tag[EM4X50_DEVICE_PASSWORD] = reflect32(*password); + } + + // only if valid em4x50 data (e.g. uid == serial) + if (tag[EM4X50_DEVICE_SERIAL] != tag[EM4X50_DEVICE_ID]) { + + // init + em4x50_setup_sim(); + gLogin = false; + gWritePasswordProcess = false; + + // start with inital command = standard read mode + command = EM4X50_COMMAND_STANDARD_READ; + + for (;;) { + + switch (command) { + + case EM4X50_COMMAND_LOGIN: + LED_B_OFF(); + LED_C_OFF(); + command = em4x50_sim_handle_login_command(tag); + break; + + case EM4X50_COMMAND_RESET: + LED_B_OFF(); + LED_C_OFF(); + command = em4x50_sim_handle_reset_command(tag); + break; + + case EM4X50_COMMAND_WRITE: + LED_B_OFF(); + LED_C_OFF(); + command = em4x50_sim_handle_write_command(tag); + break; + + case EM4X50_COMMAND_WRITE_PASSWORD: + LED_B_OFF(); + LED_C_OFF(); + command = em4x50_sim_handle_writepwd_command(tag); + break; + + case EM4X50_COMMAND_SELECTIVE_READ: + LED_B_OFF(); + LED_C_ON(); + command = em4x50_sim_handle_selective_read_command(tag); + break; + + case EM4X50_COMMAND_STANDARD_READ: + LED_B_ON(); + LED_C_OFF(); + command = em4x50_sim_handle_standard_read_command(tag); + break; + + // bit errors during reading may lead to unknown commands + // -> continue with standard read mode + default: + command = EM4X50_COMMAND_STANDARD_READ; + break; + } + + // stop if key (pm3 button or enter key) has been pressed + if (command == PM3_EOPABORTED) { + break; + } + + // if timeout (e.g. no reader field) continue with standard read + // mode and reset former authentication + if (command == PM3_ETIMEOUT) { + command = EM4X50_COMMAND_STANDARD_READ; + gLogin = false; + LED_A_OFF(); + } + } } BigBuf_free(); lf_finalize(); - reply_ng(CMD_LF_EM4X50_SIM, status, NULL, 0); + reply_ng(CMD_LF_EM4X50_SIM, command, NULL, 0); } diff --git a/armsrc/em4x50.h b/armsrc/em4x50.h index 9f9b38351..72304192b 100644 --- a/armsrc/em4x50.h +++ b/armsrc/em4x50.h @@ -19,7 +19,7 @@ void em4x50_writepwd(em4x50_data_t *etd); void em4x50_read(em4x50_data_t *etd); void em4x50_brute(em4x50_data_t *etd); void em4x50_login(uint32_t *password); -void em4x50_sim(uint8_t *filename); +void em4x50_sim(uint32_t *password); void em4x50_reader(void); void em4x50_chk(uint8_t *filename); diff --git a/client/src/cmdlfem4x50.c b/client/src/cmdlfem4x50.c index 79b019a03..84c331337 100644 --- a/client/src/cmdlfem4x50.c +++ b/client/src/cmdlfem4x50.c @@ -1129,31 +1129,66 @@ int CmdEM4x50Restore(const char *Cmd) { } int CmdEM4x50Sim(const char *Cmd) { + + int status = PM3_EFAILED; + uint32_t password = 0; + CLIParserContext *ctx; CLIParserInit(&ctx, "lf em 4x50 sim", "Simulates a EM4x50 tag.\n" "Upload using `lf em 4x50 eload`", "lf em 4x50 sim" + "lf em 4x50 sim -p 27182818 -> uses password for eload data" ); void *argtable[] = { arg_param_begin, + arg_str0("p", "passsword", "", "password, 4 bytes, lsb"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); + int pwd_len = 0; + uint8_t pwd[4] = {0}; + CLIGetHexWithReturn(ctx, 1, pwd, &pwd_len); + CLIParserFree(ctx); + + if (pwd_len) { + if (pwd_len != 4) { + PrintAndLogEx(FAILED, "password length must be 4 bytes instead of %d", pwd_len); + return PM3_EINVARG; + } else { + password = BYTES2UINT32(pwd); + } + } CLIParserFree(ctx); PrintAndLogEx(INFO, "Simulating data from emulator memory"); clearCommandBuffer(); - SendCommandNG(CMD_LF_EM4X50_SIM, NULL, 0); + SendCommandNG(CMD_LF_EM4X50_SIM, (uint8_t *)&password, sizeof(password)); PacketResponseNG resp; - WaitForResponse(CMD_LF_EM4X50_SIM, &resp); - if (resp.status == PM3_SUCCESS) + + PrintAndLogEx(INFO, "Press pm3-button to abort simulation"); + bool keypress = kbd_enter_pressed(); + while (keypress == false) { + keypress = kbd_enter_pressed(); + + if (WaitForResponseTimeout(CMD_LF_EM4X50_SIM, &resp, 1500)) { + status = resp.status; + break; + } + + } + if (keypress) { + SendCommandNG(CMD_BREAK_LOOP, NULL, 0); + status = PM3_EOPABORTED; + } + + if ((status == PM3_SUCCESS) || (status == PM3_EOPABORTED)) PrintAndLogEx(INFO, "Done"); else - PrintAndLogEx(FAILED, "No valid em4x50 data in memory."); + PrintAndLogEx(FAILED, "No valid em4x50 data in memory"); return resp.status; } @@ -1188,3 +1223,4 @@ int CmdLFEM4X50(const char *Cmd) { clearCommandBuffer(); return CmdsParse(CommandTable, Cmd); } + diff --git a/client/src/cmdlfem4x70.h b/client/src/cmdlfem4x70.h index f0f221b06..75e3e27fc 100644 --- a/client/src/cmdlfem4x70.h +++ b/client/src/cmdlfem4x70.h @@ -12,7 +12,6 @@ #define CMDLFEM4X70_H__ #include "common.h" -#include "em4x50.h" #define TIMEOUT 2000 diff --git a/include/em4x50.h b/include/em4x50.h index 573c58375..ff0090547 100644 --- a/include/em4x50.h +++ b/include/em4x50.h @@ -37,6 +37,8 @@ #define TIMEOUT 2000 #define DUMP_FILESIZE 136 +#define BYTES2UINT32(x) ((x[0] << 24) | (x[1] << 16) | (x[2] << 8) | (x[3])) + typedef struct { bool addr_given; bool pwd_given;