//----------------------------------------------------------------------------- // Tharexde, 2020 // // This code is licensed to you under the terms of the GNU GPL, version 2 or, // at your option, any later version. See the LICENSE.txt file for the text of // the license. //----------------------------------------------------------------------------- // main code for EM4x50 simulator and collector aka THAREXDE //----------------------------------------------------------------------------- #include #include "standalone.h" #include "proxmark3_arm.h" #include "appmain.h" #include "BigBuf.h" #include "fpgaloader.h" #include "util.h" #include "dbprint.h" #include "spiffs.h" #include "../em4x50.h" /* * `lf_tharexde` simulates hardcoded words/blocks, reads words of standard read * mode of EM4x50 tags and stores them in internal flash. * It requires RDV4 hardware (for flash and battery). * * On entering stand-alone mode, this module will start reading/record EM4x50 data. * Every found / collected data will be written/appended to the logfile in flash * as a text string. * * LEDs: * - LED A: simulating * - LED B: reading / record * - LED C: writing to flash * - LED D: unmounting/sync'ing flash (normally < 100ms) * * To retrieve log file from flash: * * 1. mem spiffs dump o lf_em4x50collect.log f lf_em4x50collect.log * Copies log file from flash to your client. * * 2. exit the Proxmark3 client * * 3. more lf_tharexdecollect.log * * This module emits debug strings during normal operation -- so try it out in * the lab connected to PM3 client before taking it into the field. * * To delete the log file from flash: * * 1. mem spiffs remove lf_tharexdecollect.log */ #define STATE_SIM 0 #define STATE_READ 1 #define STATE_BRUTE 2 #define EM4X50_TAG_WORD 45 #define EM4X50_PWD_SPEED 27 #define LF_EM4X50SIMULATE_INPUTFILE "lf_em4x50simulate.eml" #define LF_EM4X50COLLECT_LOGFILE "lf_em4x50collect.log" #define LF_EM4X50BRUTE_INPUTFILE "lf_em4x50brute.eml" #define LF_EM4X50BRUTE_LOGFILE "lf_em4x50brute.log" bool input_exists; bool log_exists; static void LoadDataInstructions(const char *inputfile) { Dbprintf(""); Dbprintf("To load datafile into flash and display it:"); Dbprintf(_YELLOW_("1.") " edit inputfile %s", inputfile); Dbprintf(_YELLOW_("2.") " start proxmark3 client"); Dbprintf(_YELLOW_("3.") " mem spiffs load f %s o %s", inputfile, inputfile); Dbprintf(_YELLOW_("4.") " start standalone mode"); } static void DownloadLogInstructions(const char *logfile) { Dbprintf(""); Dbprintf("To get the logfile from flash and display it:"); Dbprintf(_YELLOW_("1.") " mem spiffs dump o %s f %s", logfile, logfile); Dbprintf(_YELLOW_("2.") " exit proxmark3 client"); Dbprintf(_YELLOW_("3.") " cat %s", logfile); } static int get_input_data_from_file(uint32_t *words, char *inputfile) { size_t now = 0; if (exists_in_spiffs(inputfile)) { uint32_t size = size_in_spiffs(inputfile); uint8_t *mem = BigBuf_malloc(size); Dbprintf(_YELLOW_("found input file %s"), inputfile); rdv40_spiffs_read_as_filetype(inputfile, mem, size, RDV40_SPIFFS_SAFETY_SAFE); now = size / 9; for (int i = 0; i < now; i++) for (int j = 0; j < 4; j++) words[i] |= (hex2int(mem[2 * j + 9 * i]) << 4 | hex2int(mem[2 * j + 1 + 9 * i])) << ((3 - j) * 8); Dbprintf(_YELLOW_("read data from input file")); } BigBuf_free(); return (now > 0) ? now : 0; } static void append(const char *filename, uint8_t *entry, size_t entry_len) { LED_D_ON(); if (log_exists == false) { rdv40_spiffs_write(filename, entry, entry_len, RDV40_SPIFFS_SAFETY_SAFE); log_exists = true; } else { rdv40_spiffs_append(filename, entry, entry_len, RDV40_SPIFFS_SAFETY_SAFE); } LED_D_OFF(); } void ModInfo(void) { DbpString(_YELLOW_(" LF EM4x50 sim/collector/bruteforce mode") " - a.k.a tharexde"); } void RunMod(void) { bool state_change = true;//, password_found = false; int pwd_found = false; uint8_t state = STATE_SIM; // declarations for simulating uint32_t words[33] = {0x0}; uint32_t pwd = 0x0; uint32_t passwords[2] = {0x0}; size_t now = 0; // declarations for reading int no_words = 0; //uint32_t words[EM4X50_TAG_WORD]; uint8_t entry[81]; rdv40_spiffs_lazy_mount(); StandAloneMode(); Dbprintf(_YELLOW_("Standalone mode THAREXDE started")); for (;;) { WDT_HIT(); if (data_available()) break; // press button - toggle between SIM, READ and BRUTE // hold button - exit int button_pressed = BUTTON_CLICKED(1000); if (button_pressed == BUTTON_SINGLE_CLICK) { SpinUp(100); switch (state) { case STATE_SIM: state = STATE_READ; break; case STATE_READ: state = STATE_BRUTE; break; case STATE_BRUTE: state = STATE_SIM; break; default: break; } state_change = true; } else if (button_pressed == BUTTON_HOLD) { SpinDown(100); break; } if (state == STATE_SIM) { if (state_change) { FpgaDownloadAndGo(FPGA_BITSTREAM_LF); FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_EDGE_DETECT); FpgaSendCommand(FPGA_CMD_SET_DIVISOR, LF_DIVISOR_125); 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; LEDsoff(); LED_A_ON(); Dbprintf(""); Dbprintf(_YELLOW_("switched to EM4x50 simulating mode")); now = get_input_data_from_file(words, LF_EM4X50SIMULATE_INPUTFILE); if (now > 0) { Dbprintf(_YELLOW_("simulating %i blocks"), now); for (int i = 0; i < now; i++) Dbprintf("%2i -> %lx", i + 1, words[i]); } else { Dbprintf(_RED_("error in input data")); } state_change = false; } em4x50_sim_send_listen_window(); for (int i = 0; i < now; i++) { em4x50_sim_send_listen_window(); em4x50_sim_send_word(words[i]); } } else if (state == STATE_READ) { if (state_change) { LEDsoff(); LED_B_ON(); Dbprintf(""); Dbprintf(_YELLOW_("switched to EM4x50 reading mode")); memset(entry, 0, sizeof(entry)); memset(words, 0, sizeof(words)); log_exists = exists_in_spiffs(LF_EM4X50COLLECT_LOGFILE); state_change = false; } no_words = em4x50_standalone_read(words); if (no_words > 0) { memset(entry, 0, sizeof(entry)); sprintf((char *)entry, "found new EM4x50 tag:"); Dbprintf("%s", entry); strcat((char *)entry, "\n"); append(LF_EM4X50COLLECT_LOGFILE, entry, strlen((char *)entry)); for (int i = 0; i < no_words; i++) { sprintf((char *)entry, " %2i -> 0x%08"PRIx32"", i + 1, words[i]); Dbprintf("%s", entry); strcat((char *)entry, "\n"); append(LF_EM4X50COLLECT_LOGFILE, entry, strlen((char *)entry)); } } } else if (state == STATE_BRUTE) { if (state_change) { LEDsoff(); LED_C_ON(); Dbprintf(""); Dbprintf(_YELLOW_("switched to EM4x50 brute force mode")); log_exists = exists_in_spiffs(LF_EM4X50BRUTE_LOGFILE); now = get_input_data_from_file(passwords, LF_EM4X50BRUTE_INPUTFILE); if (now == 2) { // print some information int no_iter = passwords[1] - passwords[0] + 1; int dur_s = no_iter / EM4X50_PWD_SPEED; int dur_h = dur_s / 3600; int dur_m = (dur_s - dur_h * 3600) / 60; dur_s -= dur_h * 3600 + dur_m * 60; //iterprint = no_iter/10; Dbprintf(_YELLOW_("trying %i passwords in range [0x%08x, 0x%08x]"), no_iter, passwords[0], passwords[1]); Dbprintf(_YELLOW_("estimated duration: %ih%im%is"), dur_h, dur_m, dur_s); } else { Dbprintf(_RED_("error in input data")); break; } state_change = false; } pwd_found = em4x50_standalone_brute(passwords[0], passwords[1], &pwd); if (pwd_found == PM3_ETIMEOUT) { // timeout -> no EM4x50 tag on reader? Dbprintf(_YELLOW_("timeout - no EM4x50 tag detected")); } else if (pwd_found == true) { // password found -> write to logfile sprintf((char *)entry, "password found: 0x%08"PRIx32, pwd); Dbprintf(_YELLOW_("%s"), entry); strcat((char *)entry, "\n"); append(LF_EM4X50BRUTE_LOGFILE, entry, strlen((char *)entry)); break; } else { if (pwd == passwords[1] + 1) { // finished without success -> write to logfile sprintf((char *)entry, "no password found"); Dbprintf(_YELLOW_("%s"), entry); strcat((char *)entry, "\n"); append(LF_EM4X50BRUTE_LOGFILE, entry, strlen((char *)entry)); } else { // stopped -> write to logfile sprintf((char *)entry, "stopped search - last password: 0x%08"PRIx32, pwd); Dbprintf(_YELLOW_("%s"), entry); strcat((char *)entry, "\n"); append(LF_EM4X50BRUTE_LOGFILE, entry, strlen((char *)entry)); // replace start password by last tested password in // inputfile (spiffs) so that brute forcing process will // be continued when envoking brute force mode again sprintf((char *)entry, "%08"PRIx32"\n%08"PRIx32"\n", pwd, passwords[1]); rdv40_spiffs_write(LF_EM4X50BRUTE_INPUTFILE, entry, strlen((char *)entry), RDV40_SPIFFS_SAFETY_SAFE); } break; } } } if (state == STATE_READ) { DownloadLogInstructions(LF_EM4X50COLLECT_LOGFILE); } else if (state == STATE_BRUTE) { LoadDataInstructions(LF_EM4X50BRUTE_INPUTFILE); DownloadLogInstructions(LF_EM4X50BRUTE_LOGFILE); } else { LoadDataInstructions(LF_EM4X50SIMULATE_INPUTFILE); } LED_D_ON(); rdv40_spiffs_lazy_unmount(); LED_D_OFF(); FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LEDsoff(); Dbprintf(""); Dbprintf(_YELLOW_("[=] Standalone mode THAREXDE stopped")); }