1 //-----------------------------------------------------------------------------
2 // Copyright (C) tharexde, 2021
3 // Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
5 // This program is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation, either version 3 of the License, or
8 // (at your option) any later version.
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // See LICENSE.txt for the text of the license.
16 //-----------------------------------------------------------------------------
17 // main code for EM4x50 simulator and collector aka THAREXDE
18 //-----------------------------------------------------------------------------
21 #include "standalone.h"
22 #include "proxmark3_arm.h"
25 #include "commonutil.h"
26 #include "fpgaloader.h"
30 #include "../em4x50.h"
33 * `lf_tharexde` simulates EM4x50 dumps uploaded to flash, reads words
34 * transmitted by EM4x50 tags in standard read mode and stores them in
36 * It requires RDV4 hardware (for flash and battery).
38 * On entering stand-alone mode, this module will start simulating EM4x50 data.
39 * Data is read from eml dump file uploaded to flash memory (lf_em4x50_simulate.eml).
40 * If reader sends password different from dump file password, it is saved in
41 * file lf_em4x50_passwords.log in flash memory.
43 * On switching to read/record mode by pressing pm3 button, module will start
44 * reading EM4x50 data. Each collected data set will be written/appended to the
45 * logfile in flash (lf_em4x50_collect.log) as a text string.
49 * - LED A blinking: no simulation data or read error
50 * - LED B: reading/recording
51 * - LED D: unmounting/sync'ing flash (normally < 100ms)
53 * To upload input file (eml format) to flash:
54 * - mem spiffs upload -s <filename> -d lf_em4x50_simulate.eml
56 * To retrieve password file from flash:
57 * - mem spiffs dump -s lf_em4x50_passwords.log
59 * To retrieve log file from flash:
60 * - mem spiffs dump -s lf_em4x50_collect.log
62 * This module emits debug strings during normal operation -- so try it out in
63 * the lab connected to PM3 client before taking it into the field.
65 * To delete the input file from flash:
66 * - mem spiffs remove -f lf_em4x50_simulate.eml
68 * To delete the log file from flash:
69 * - mem spiffs remove -f lf_em4x50_passwords.log
71 * To delete the log file from flash:
72 * - mem spiffs remove -f lf_em4x50_collect.log
77 #define LF_EM4X50_INPUTFILE_SIM "lf_em4x50_simulate.eml"
78 #define LF_EM4X50_LOGFILE_SIM "lf_em4x50_passwords.log"
79 #define LF_EM4X50_LOGFILE_COLLECT "lf_em4x50_collect.log"
80 #define MAX_NO_PWDS_TO_SAVE 50
82 static void LoadDataInstructions(const char *inputfile
) {
84 Dbprintf("To load datafile to flash and display it:");
85 Dbprintf("1. edit input file %s", inputfile
);
86 Dbprintf("2. start proxmark3 client");
87 Dbprintf("3. mem spiffs upload -s <filename> -d %s", inputfile
);
88 Dbprintf("4. start standalone mode");
91 static void DownloadLogInstructions(const char *logfile
) {
93 Dbprintf("To get the logfile from flash and display it:");
94 Dbprintf("1. mem spiffs dump -s %s", logfile
);
95 Dbprintf("2. exit proxmark3 client");
96 Dbprintf("3. cat <filename>");
99 static bool get_input_data_from_file(uint32_t *tag
, char *inputfile
) {
103 if (exists_in_spiffs(inputfile
)) {
105 uint32_t size
= size_in_spiffs(inputfile
);
106 uint8_t *mem
= BigBuf_malloc(size
);
108 Dbprintf(_YELLOW_("found input file %s"), inputfile
);
110 rdv40_spiffs_read_as_filetype(inputfile
, mem
, size
, RDV40_SPIFFS_SAFETY_SAFE
);
112 now
= (size
+ 1) / 9;
113 for (int i
= 0; i
< now
; i
++) {
114 for (int j
= 0; j
< 4; j
++) {
115 tag
[i
] |= (hex2int(mem
[2 * j
+ 9 * i
]) << 4 | hex2int(mem
[2 * j
+ 1 + 9 * i
])) << ((3 - j
) * 8);
119 Dbprintf(_YELLOW_("read tag data from input file"));
121 Dbprintf(_RED_("no input file %s"), inputfile
);
126 return ((now
== EM4X50_NO_WORDS
) && (tag
[EM4X50_DEVICE_SERIAL
] != tag
[EM4X50_DEVICE_ID
]));
129 static void append(const char *filename
, uint8_t *entry
, size_t entry_len
) {
130 if (exists_in_spiffs(filename
)) {
131 rdv40_spiffs_append(filename
, entry
, entry_len
, RDV40_SPIFFS_SAFETY_SAFE
);
133 rdv40_spiffs_write(filename
, entry
, entry_len
, RDV40_SPIFFS_SAFETY_SAFE
);
137 static void save_pwds(uint32_t *pwdlist
, size_t no_pwd
) {
140 for (int i
= 0; i
< no_pwd
; i
++) {
141 uint8_t entry
[10] = {0};
142 sprintf((char *)entry
, "%08"PRIx32
"\n", pwdlist
[i
]);
143 append(LF_EM4X50_LOGFILE_SIM
, entry
, strlen((char *)entry
));
144 Dbprintf("received password: %08"PRIx32
"", pwdlist
[i
]);
150 DbpString(_YELLOW_(" LF EM4x50 sim/collector mode") " - a.k.a tharexde");
155 bool state_change
= true, read_ok
= false;
156 int no_words
= 0, command
= 0, no_pwd
= 0;
157 uint8_t entry
[400], state
= STATE_SIM
;
158 uint32_t tag
[EM4X50_NO_WORDS
] = {0x0}, pwdlist
[MAX_NO_PWDS_TO_SAVE
];
160 rdv40_spiffs_lazy_mount();
162 Dbprintf(_YELLOW_("Standalone mode THAREXDE started"));
167 if (data_available()) {
171 // press button - toggle between SIM and READ
172 // hold button - exit
173 int button_pressed
= BUTTON_CLICKED(1000);
174 if (button_pressed
== BUTTON_SINGLE_CLICK
) {
178 // save and display passwords
179 save_pwds(pwdlist
, no_pwd
);
191 } else if (button_pressed
== BUTTON_HOLD
) {
195 if (state
== STATE_SIM
) {
199 // initialize simulation mode
203 Dbprintf(_YELLOW_("switched to EM4x50 simulating mode"));
205 read_ok
= get_input_data_from_file(tag
, LF_EM4X50_INPUTFILE_SIM
);
207 Dbprintf(_YELLOW_("tag data ok"));
209 Dbprintf(_RED_("error in tag data"));
210 LoadDataInstructions(LF_EM4X50_INPUTFILE_SIM
);
215 g_Password
= reflect32(tag
[0]);
216 g_WritePasswordProcess
= false;
217 command
= EM4X50_COMMAND_STANDARD_READ
;
219 memset(pwdlist
, 0, sizeof(pwdlist
));
222 state_change
= false;
225 // if no data or read error -> blink
226 if (read_ok
== false) {
231 em4x50_handle_commands(&command
, tag
, true);
233 // check if new password was found
234 if (g_Password
!= reflect32(tag
[EM4X50_DEVICE_PASSWORD
])) {
235 if (no_pwd
< MAX_NO_PWDS_TO_SAVE
) {
236 pwdlist
[no_pwd
] = g_Password
;
239 g_Password
= reflect32(tag
[EM4X50_DEVICE_PASSWORD
]);
242 // if timeout (e.g. no reader field) continue with standard read
243 // mode and reset former authentication
244 if (command
== PM3_ETIMEOUT
) {
245 command
= EM4X50_COMMAND_STANDARD_READ
;
250 } else if (state
== STATE_READ
) {
254 // initialize read mode
258 Dbprintf(_YELLOW_("switched to EM4x50 reading mode"));
261 state_change
= false;
265 memset(tag
, 0, sizeof(tag
));
266 standard_read(&no_words
, tag
);
270 memset(entry
, 0, sizeof(entry
));
272 sprintf((char *)entry
, "found EM4x50 tag:\n");
273 for (int i
= 0; i
< no_words
; i
++) {
274 sprintf((char *)entry
+ strlen((char *)entry
), "%08"PRIx32
"\n", tag
[i
]);
276 Dbprintf("%s", entry
);
277 sprintf((char *)entry
+ strlen((char *)entry
), "\n");
278 append(LF_EM4X50_LOGFILE_COLLECT
, entry
, strlen((char *)entry
));
283 AT91C_BASE_TC1
->TC_CCR
= AT91C_TC_CLKEN
| AT91C_TC_SWTRG
; // re-enable timer and wait for TC0
284 AT91C_BASE_TC0
->TC_RC
= 0; // set TIOA (carry bit) on overflow, return to zero
285 AT91C_BASE_TC0
->TC_RA
= 1; // clear carry bit on next clock cycle
286 AT91C_BASE_TC0
->TC_CCR
= AT91C_TC_CLKEN
| AT91C_TC_SWTRG
; // reset and re-enable timer
289 if (state
== STATE_READ
) {
290 DownloadLogInstructions(LF_EM4X50_LOGFILE_COLLECT
);
292 // save and display passwords
293 save_pwds(pwdlist
, no_pwd
);
294 DownloadLogInstructions(LF_EM4X50_LOGFILE_SIM
);
298 rdv40_spiffs_lazy_unmount();
301 FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF
);
305 Dbprintf(_YELLOW_("[=] Standalone mode THAREXDE stopped"));