From db679608ecab0dc646a887cc6ee430588978c9ca Mon Sep 17 00:00:00 2001 From: tharexde Date: Mon, 15 Feb 2021 22:44:17 +0100 Subject: [PATCH] initial implementation of em4x50 standalone mode (lf_tharexde) --- CHANGELOG.md | 1 + armsrc/Standalone/Makefile.hal | 7 +- armsrc/Standalone/Makefile.inc | 4 + armsrc/Standalone/lf_tharexde.c | 283 ++++++++++++++++++ .../4_Advanced-compilation-parameters.md | 1 + 5 files changed, 294 insertions(+), 2 deletions(-) create mode 100644 armsrc/Standalone/lf_tharexde.c diff --git a/CHANGELOG.md b/CHANGELOG.md index 9547aba77..b730df269 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 `LF_THAREXDE` standalone mode which simulates and reads EM4x50 cards (@tharexde) - Added `hf jooki` commands (@iceman1001) - Changed `wiegand encode` - format param is now optional, w/o it will try encode all formats (@iceman1001) - Fix cppchecker warnings (@iceman1001) diff --git a/armsrc/Standalone/Makefile.hal b/armsrc/Standalone/Makefile.hal index d5414f661..552267611 100644 --- a/armsrc/Standalone/Makefile.hal +++ b/armsrc/Standalone/Makefile.hal @@ -35,6 +35,9 @@ define KNOWN_STANDALONE_DEFINITIONS | LF_SAMYRUN | HID26 read/clone/sim | | | - Samy Kamkar | +----------------------------------------------------------+ +| LF_THAREXDE | Simulate/read EM4x50 tags | +| (RDV4 only) | storing in flashmem | ++----------------------------------------------------------+ | HF_14ASNIFF | 14a sniff to flashmem | | (RDV4 only) | | +----------------------------------------------------------+ @@ -67,10 +70,10 @@ define KNOWN_STANDALONE_DEFINITIONS +----------------------------------------------------------+ endef -STANDALONE_MODES := LF_SKELETON LF_EM4100EMUL LF_EM4100RSWB LF_EM4100RWC LF_HIDBRUTE LF_ICEHID LF_PROXBRUTE LF_SAMYRUN +STANDALONE_MODES := LF_SKELETON LF_EM4100EMUL LF_EM4100RSWB LF_EM4100RWC LF_HIDBRUTE LF_ICEHID LF_PROXBRUTE LF_SAMYRUN LF_THAREXDE STANDALONE_MODES += HF_14ASNIFF HF_AVEFUL HF_BOG HF_COLIN HF_ICECLASS HF_LEGIC HF_MATTYRUN HF_MSDSAL HF_TCPRST HF_YOUNG STANDALONE_MODES_REQ_SMARTCARD := -STANDALONE_MODES_REQ_FLASH := LF_ICEHID HF_14ASNIFF HF_BOG HF_COLIN HF_ICECLASS +STANDALONE_MODES_REQ_FLASH := LF_ICEHID LF_THAREXDE HF_14ASNIFF HF_BOG HF_COLIN HF_ICECLASS ifneq ($(filter $(STANDALONE),$(STANDALONE_MODES)),) STANDALONE_PLATFORM_DEFS += -DWITH_STANDALONE_$(STANDALONE) ifneq ($(filter $(STANDALONE),$(STANDALONE_MODES_REQ_SMARTCARD)),) diff --git a/armsrc/Standalone/Makefile.inc b/armsrc/Standalone/Makefile.inc index 087a4d927..519dc2087 100644 --- a/armsrc/Standalone/Makefile.inc +++ b/armsrc/Standalone/Makefile.inc @@ -73,3 +73,7 @@ endif ifneq (,$(findstring WITH_STANDALONE_HF_ICECLASS,$(APP_CFLAGS))) SRC_STANDALONE = hf_iceclass.c endif +# WITH_STANDALONE_LF_THAREXDE +ifneq (,$(findstring WITH_STANDALONE_LF_THAREXDE,$(APP_CFLAGS))) + SRC_STANDALONE = lf_tharexde.c +endif diff --git a/armsrc/Standalone/lf_tharexde.c b/armsrc/Standalone/lf_tharexde.c new file mode 100644 index 000000000..c88437f4f --- /dev/null +++ b/armsrc/Standalone/lf_tharexde.c @@ -0,0 +1,283 @@ +//----------------------------------------------------------------------------- +// tharexde, 2021 +// +// 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 "ticks.h" +#include "standalone.h" +#include "proxmark3_arm.h" +#include "appmain.h" +#include "BigBuf.h" +#include "commonutil.h" +#include "fpgaloader.h" +#include "util.h" +#include "dbprint.h" +#include "spiffs.h" +#include "../em4x50.h" + +/* + * `lf_tharexde` simulates EM4x50 dumps uploaded to flash, reads words + * transmitted by EM4x50 tags in standard read mode and stores them in + * internal flash. + * It requires RDV4 hardware (for flash and battery). + * + * On entering stand-alone mode, this module will start simulating EM4x50 data. + * Data is read from eml dump file uploaded to flash memory (lf_em4x50_simulate.eml). + * If reader sends password different from dump file password, it is saved in + * file lf_em4x50_passwords.log in flash memory. + * + * On switching to read/record mode by pressing pm3 button, module will start + * reading EM4x50 data. Each collected data set will be written/appended to the + * logfile in flash (lf_em4x50_collect.log) as a text string. + * + * LEDs: + * - LED A: simulating + * - LED A blinking: no simulation data or read error + * - LED B: reading/recording + * - LED D: unmounting/sync'ing flash (normally < 100ms) + * + * To upload input file (eml format) to flash: + * - mem spiffs load f o lf_em4x50_simulate.eml + * + * To retrieve password file from flash: + * - mem spiffs dump o lf_em4x50_passwords.log f + * + * To retrieve log file from flash: + * - mem spiffs dump o lf_em4x50_collect.log f + * + * 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 input file from flash: + * - mem spiffs remove lf_em4x50_simulate.eml + * + * To delete the log file from flash: + * - mem spiffs remove lf_em4x50_passwords.log + * + * To delete the log file from flash: + * - mem spiffs remove lf_em4x50_collect.log + */ + +#define STATE_SIM 0 +#define STATE_READ 1 +#define LF_EM4X50_INPUTFILE_SIM "lf_em4x50_simulate.eml" +#define LF_EM4X50_LOGFILE_SIM "lf_em4x50_passwords.log" +#define LF_EM4X50_LOGFILE_COLLECT "lf_em4x50_collect.log" + +uint32_t gPassword; + +static void LoadDataInstructions(const char *inputfile) { + Dbprintf(""); + Dbprintf("To load datafile to flash and display it:"); + Dbprintf("1. edit input file %s", inputfile); + Dbprintf("2. start proxmark3 client"); + Dbprintf("3. mem spiffs load f o %s", inputfile); + Dbprintf("4. start standalone mode"); +} + +static void DownloadLogInstructions(const char *logfile) { + Dbprintf(""); + Dbprintf("To get the logfile from flash and display it:"); + Dbprintf("1. mem spiffs dump o %s f ", logfile); + Dbprintf("2. exit proxmark3 client"); + Dbprintf("3. cat "); +} + +static bool get_input_data_from_file(uint32_t *tag, 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++) { + tag[i] |= (hex2int(mem[2 * j + 9 * i]) << 4 | hex2int(mem[2 * j + 1 + 9 * i])) << ((3 - j) * 8); + } + } + + Dbprintf(_YELLOW_("read tag data from input file")); + } else { + Dbprintf(_RED_("no input file %s"), inputfile); + } + + BigBuf_free(); + + return ((now == EM4X50_NO_WORDS) && (tag[EM4X50_DEVICE_SERIAL] != tag[EM4X50_DEVICE_ID])); +} + +static void append(const char *filename, uint8_t *entry, size_t entry_len) { + if (exists_in_spiffs(filename)) { + rdv40_spiffs_append(filename, entry, entry_len, RDV40_SPIFFS_SAFETY_SAFE); + } else { + rdv40_spiffs_write(filename, entry, entry_len, RDV40_SPIFFS_SAFETY_SAFE); + } +} + +void ModInfo(void) { + DbpString(_YELLOW_(" LF EM4x50 sim/collector mode") " - a.k.a tharexde"); +} + +void RunMod(void) { + + bool state_change = true, read_ok = false; + int no_words = 0, command = 0; + uint8_t entry[400], state = STATE_SIM; + uint32_t tag[EM4X50_NO_WORDS] = {0x0}; + + rdv40_spiffs_lazy_mount(); + + StandAloneMode(); + Dbprintf(_YELLOW_("Standalone mode THAREXDE started")); + + for (;;) { + + WDT_HIT(); + if (data_available()) { + break; + } + + // press button - toggle between SIM and READ + // hold button - exit + int button_pressed = BUTTON_CLICKED(1000); + if (button_pressed == BUTTON_SINGLE_CLICK) { + + switch (state) { + case STATE_SIM: + state = STATE_READ; + break; + case STATE_READ: + state = STATE_SIM; + break; + default: + break; + } + + state_change = true; + + } else if (button_pressed == BUTTON_HOLD) { + break; + } + + if (state == STATE_SIM) { + + if (state_change) { + + LEDsoff(); + LED_A_ON(); + Dbprintf(""); + Dbprintf(_YELLOW_("switched to EM4x50 simulating mode")); + + read_ok = get_input_data_from_file(tag, LF_EM4X50_INPUTFILE_SIM); + if (read_ok) { + Dbprintf(_YELLOW_("tag data ok")); + } else { + Dbprintf(_RED_("error in tag data")); + LoadDataInstructions(LF_EM4X50_INPUTFILE_SIM); + } + + // init; start with command = standard read mode + em4x50_setup_sim(); + gLogin = false; + LED_D_OFF(); + gPassword = reflect32(tag[0]); + gWritePasswordProcess = false; + command = EM4X50_COMMAND_STANDARD_READ; + + state_change = false; + } + + // if no data or read error -> blink + if (read_ok == false) { + LED(LED_A, 200); + SpinDelay(200); + } + + em4x50_handle_commands(&command, tag); + + // check if new password was found + if (gPassword != reflect32(tag[EM4X50_DEVICE_PASSWORD])) { + + Dbprintf("received password: %08"PRIx32"", gPassword); + + // append password to logfile in flash memory + memset(entry, 0, sizeof(entry)); + sprintf((char *)entry, "%08"PRIx32"\n", gPassword); + append(LF_EM4X50_LOGFILE_SIM, entry, strlen((char *)entry)); + + gPassword = reflect32(tag[EM4X50_DEVICE_PASSWORD]); + } + + // 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_D_OFF(); + } + + } else if (state == STATE_READ) { + + if (state_change) { + + LEDsoff(); + LED_B_ON(); + Dbprintf(""); + Dbprintf(_YELLOW_("switched to EM4x50 reading mode\n")); + + em4x50_setup_read(); + state_change = false; + } + + no_words = 0; + memset(tag, 0, sizeof(tag)); + standard_read(&no_words, tag); + + if (no_words > 0) { + + memset(entry, 0, sizeof(entry)); + + sprintf((char *)entry, "found EM4x50 tag:\n"); + for (int i = 0; i < no_words; i++) { + sprintf((char *)entry + strlen((char *)entry), "%08"PRIx32"\n", tag[i]); + } + Dbprintf("%s", entry); + sprintf((char *)entry + strlen((char *)entry), "\n"); + append(LF_EM4X50_LOGFILE_COLLECT, entry, strlen((char *)entry)); + } + } + + // reset timer + AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; // re-enable timer and wait for TC0 + AT91C_BASE_TC0->TC_RC = 0; // set TIOA (carry bit) on overflow, return to zero + AT91C_BASE_TC0->TC_RA = 1; // clear carry bit on next clock cycle + AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; // reset and re-enable timer + } + + if (state == STATE_READ) { + DownloadLogInstructions(LF_EM4X50_LOGFILE_COLLECT); + } else { + DownloadLogInstructions(LF_EM4X50_LOGFILE_SIM); + } + + LED_D_ON(); + rdv40_spiffs_lazy_unmount(); + LED_D_OFF(); + + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LEDsoff(); + Dbprintf(""); + Dbprintf(_YELLOW_("[=] Standalone mode THAREXDE stopped")); +} diff --git a/doc/md/Use_of_Proxmark/4_Advanced-compilation-parameters.md b/doc/md/Use_of_Proxmark/4_Advanced-compilation-parameters.md index 6dbe54d49..2ba6aeaba 100644 --- a/doc/md/Use_of_Proxmark/4_Advanced-compilation-parameters.md +++ b/doc/md/Use_of_Proxmark/4_Advanced-compilation-parameters.md @@ -79,6 +79,7 @@ Here are the supported values you can assign to `STANDALONE` in `Makefile.platfo |-----------------|----------------------------------------| | | No standalone mode | LF_SKELETON | standalone mode skeleton - Iceman1001 +| LF_THAREXDE | LF EM4x50 simulator/read standalone mode - tharexde | LF_EM4100EMUL | LF EM4100 simulator standalone mode - temskiy | LF_EM4100RSWB | LF EM4100 read/write/clone/brute mode - Monster1024 | LF_EM4100RWC | LF EM4100 read/write/clone mode - temskiy