fix one too small
[RRG-proxmark3.git] / client / src / cmdlfem4x50.c
blobaa128b53a143dc1ca0dfb4826a06f6b688b7e338
1 //-----------------------------------------------------------------------------
2 // Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
14 // See LICENSE.txt for the text of the license.
15 //-----------------------------------------------------------------------------
16 // Low frequency EM4x50 commands
17 //-----------------------------------------------------------------------------
19 #include "cliparser.h"
20 #include "cmdlfem4x50.h"
21 #include <ctype.h>
22 #include <math.h>
23 #include "cmdparser.h" // command_t
24 #include "util_posix.h" // msclock
25 #include "fileutils.h"
26 #include "commonutil.h"
27 #include "pmflash.h"
28 #include "cmdflashmemspiffs.h"
29 #include "em4x50.h"
31 static int CmdHelp(const char *Cmd);
33 static void em4x50_prepare_result(const uint8_t *data, int fwr, int lwr, em4x50_word_t *words) {
35 // restructure received result in "em4x50_word_t" structure
36 for (int i = fwr; i <= lwr; i++) {
37 for (int j = 0; j < 4; j++) {
38 words[i].byte[j] = data[i * 4 + (3 - j)];
43 static void em4x50_print_result(const em4x50_word_t *words, int fwr, int lwr) {
45 // print available information for given word from fwr to lwr, i.e.
46 // bit table + summary lines with hex notation of word (msb + lsb)
48 PrintAndLogEx(NORMAL, "");
49 PrintAndLogEx(INFO, " # | word (msb) | word (lsb) | desc");
50 PrintAndLogEx(INFO, "----+-------------+-------------+--------------------");
52 for (int i = fwr; i <= lwr; i++) {
54 const char *s;
55 switch (i) {
56 case EM4X50_DEVICE_PASSWORD:
57 s = _YELLOW_("password ( WO )");
58 break;
59 case EM4X50_PROTECTION:
60 s = _YELLOW_("protection cfg ( locked )");
61 break;
62 case EM4X50_CONTROL:
63 s = _YELLOW_("control cfg ( locked )");
64 break;
65 case EM4X50_DEVICE_SERIAL:
66 s = _YELLOW_("serial number ( RO )");
67 break;
68 case EM4X50_DEVICE_ID:
69 s = _YELLOW_("device id ( RO )");
70 break;
71 default:
72 s = "user data";
73 break;
76 char r[30] = {0};
77 for (int j = 3; j >= 0; j--) {
78 int offset = strlen(r);
79 snprintf(r + offset, sizeof(r) - offset, "%02x ", reflect8(words[i].byte[j]));
82 PrintAndLogEx(INFO, " %2i | " _GREEN_("%s") "| %s| %s",
84 sprint_hex(words[i].byte, 4),
89 PrintAndLogEx(INFO, "----+-------------+-------------+--------------------");
92 static void em4x50_print_info_result(uint8_t *data, bool verbose) {
94 // display all information of info result in structured format
95 em4x50_word_t words[EM4X50_NO_WORDS];
96 em4x50_prepare_result(data, 0, EM4X50_NO_WORDS - 1, words);
98 bool bpwc = words[EM4X50_CONTROL].byte[CONFIG_BLOCK] & PASSWORD_CHECK;
99 bool braw = words[EM4X50_CONTROL].byte[CONFIG_BLOCK] & READ_AFTER_WRITE;
101 int fwr = reflect8(words[EM4X50_CONTROL].byte[FIRST_WORD_READ]);
102 int lwr = reflect8(words[EM4X50_CONTROL].byte[LAST_WORD_READ]);
103 int fwrp = reflect8(words[EM4X50_PROTECTION].byte[FIRST_WORD_READ_PROTECTED]);
104 int lwrp = reflect8(words[EM4X50_PROTECTION].byte[LAST_WORD_READ_PROTECTED]);
105 int fwwi = reflect8(words[EM4X50_PROTECTION].byte[FIRST_WORD_WRITE_INHIBITED]);
106 int lwwi = reflect8(words[EM4X50_PROTECTION].byte[LAST_WORD_WRITE_INHIBITED]);
108 PrintAndLogEx(NORMAL, "");
109 PrintAndLogEx(INFO, "--- " _CYAN_("Tag Information") " ---------------------------");
111 // data section
112 if (verbose) {
113 em4x50_print_result(words, 0, EM4X50_NO_WORDS - 1);
114 } else {
115 em4x50_print_result(words, EM4X50_DEVICE_SERIAL, EM4X50_DEVICE_ID);
118 // configuration section
119 PrintAndLogEx(NORMAL, "");
120 PrintAndLogEx(INFO, "---- " _CYAN_("Configuration") " ----");
122 PrintAndLogEx(INFO, "first word read.... " _YELLOW_("%i"), fwr);
123 PrintAndLogEx(INFO, "last word read..... " _YELLOW_("%i"), lwr);
124 PrintAndLogEx(INFO, "password check..... %s", (bpwc) ? _RED_("on") : _GREEN_("off"));
125 PrintAndLogEx(INFO, "read after write... %s", (braw) ? "on" : "off");
126 PrintAndLogEx(NORMAL, "");
127 PrintAndLogEx(INFO, "--------- " _CYAN_("Protection") " ------------");
128 PrintAndLogEx(INFO, "first word read protected.... %i", fwrp);
129 PrintAndLogEx(INFO, "last word read protected..... %i", lwrp);
130 PrintAndLogEx(INFO, "first word write inhibited... %i", fwwi);
131 PrintAndLogEx(INFO, "last word write inhibited.... %i", lwwi);
132 PrintAndLogEx(NORMAL, "");
133 PrintAndLogEx(INFO, "zero values may indicate read protection");
134 PrintAndLogEx(NORMAL, "");
137 static int em4x50_load_file(const char *filename, uint8_t *data, size_t data_len, size_t *bytes_read) {
139 // read dump file
140 uint8_t *dump = NULL;
141 *bytes_read = 0;
142 int res = pm3_load_dump(filename, (void **)&dump, bytes_read, EM4X50_DUMP_FILESIZE);
143 if (res != PM3_SUCCESS) {
144 return res;
147 if (*bytes_read != EM4X50_DUMP_FILESIZE) {
148 free(dump);
149 return PM3_EFILE;
152 // sanity check, valid em4x50 data?
153 uint32_t serial = bytes_to_num(dump + 4 * EM4X50_DEVICE_SERIAL, 4);
154 uint32_t device_id = bytes_to_num(dump + 4 * EM4X50_DEVICE_ID, 4);
155 if (serial == device_id) {
156 PrintAndLogEx(WARNING, "No valid EM4x50 data in file %s", filename);
157 free(dump);
158 return PM3_ENODATA;
161 memcpy(data, dump, *bytes_read);
162 free(dump);
163 return PM3_SUCCESS;
166 static void em4x50_seteml(uint8_t *src, uint32_t offset, uint32_t numofbytes) {
168 PrintAndLogEx(INFO, "uploading to emulator memory");
169 PrintAndLogEx(INFO, "." NOLF);
170 // fast push mode
171 g_conn.block_after_ACK = true;
172 for (size_t i = offset; i < numofbytes; i += PM3_CMD_DATA_SIZE_MIX) {
174 size_t len = MIN((numofbytes - i), PM3_CMD_DATA_SIZE_MIX);
175 if (len == numofbytes - i) {
176 // Disable fast mode on last packet
177 g_conn.block_after_ACK = false;
179 clearCommandBuffer();
180 SendCommandMIX(CMD_LF_EM4X50_ESET, i, len, 0, src + i, len);
181 PrintAndLogEx(NORMAL, "." NOLF);
182 fflush(stdout);
184 PrintAndLogEx(NORMAL, "");
185 PrintAndLogEx(SUCCESS, "uploaded " _YELLOW_("%d") " bytes to emulator memory", numofbytes);
188 static int CmdEM4x50ELoad(const char *Cmd) {
189 CLIParserContext *ctx;
190 CLIParserInit(&ctx, "lf em 4x50 eload",
191 "Loads EM4x50 tag dump (bin/eml/json) into emulator memory on device",
192 "lf em 4x50 eload -f mydump.bin\n"
195 void *argtable[] = {
196 arg_param_begin,
197 arg_str1("f", "file", "<fn>", "Specify a filename for dump file"),
198 arg_param_end
201 CLIExecWithReturn(ctx, Cmd, argtable, true);
202 int fnlen = 0;
203 char filename[FILE_PATH_SIZE] = {0};
204 CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen);
205 CLIParserFree(ctx);
207 // read data from dump file; file type has to be "bin", "eml" or "json"
208 size_t bytes_read = 0;
209 uint8_t data[EM4X50_DUMP_FILESIZE] = {0x0};
211 if (em4x50_load_file(filename, data, EM4X50_DUMP_FILESIZE, &bytes_read) != PM3_SUCCESS) {
212 PrintAndLogEx(FAILED, "Read error");
213 return PM3_EFILE;
216 // upload to emulator memory
217 em4x50_seteml(data, 0, EM4X50_DUMP_FILESIZE);
218 PrintAndLogEx(HINT, "You are ready to simulate. See " _YELLOW_("`lf em 4x50 sim -h`"));
219 PrintAndLogEx(INFO, "Done!");
220 return PM3_SUCCESS;
223 static int CmdEM4x50ESave(const char *Cmd) {
224 CLIParserContext *ctx;
225 CLIParserInit(&ctx, "lf em 4x50 esave",
226 "Saves bin/json dump file of emulator memory.",
227 "lf em 4x50 esave -> use UID as filename\n"
228 "lf em 4x50 esave -f mydump\n"
231 void *argtable[] = {
232 arg_param_begin,
233 arg_str0("f", "file", "<fn>", "specifiy filename"),
234 arg_param_end
237 CLIExecWithReturn(ctx, Cmd, argtable, true);
238 int fnlen = 0;
239 char filename[FILE_PATH_SIZE] = {0};
240 CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen);
241 CLIParserFree(ctx);
243 // download emulator memory
244 PrintAndLogEx(SUCCESS, "Reading emulator memory...");
245 uint8_t data[EM4X50_DUMP_FILESIZE] = {0x0};
246 if (GetFromDevice(BIG_BUF_EML, data, EM4X50_DUMP_FILESIZE, 0, NULL, 0, NULL, 2500, false) == false) {
247 PrintAndLogEx(WARNING, "Fail, transfer from device time-out");
248 return PM3_ETIMEOUT;
251 // valid em4x50 data?
252 uint32_t serial = bytes_to_num(data + 4 * EM4X50_DEVICE_SERIAL, 4);
253 uint32_t device_id = bytes_to_num(data + 4 * EM4X50_DEVICE_ID, 4);
254 if (serial == device_id) {
255 PrintAndLogEx(WARNING, "No valid em4x50 data in flash memory.");
256 return PM3_ENODATA;
259 // user supplied filename?
260 if (fnlen == 0) {
261 PrintAndLogEx(INFO, "Using UID as filename");
262 char *fptr = filename;
263 fptr += snprintf(fptr, sizeof(filename), "lf-4x50-");
264 FillFileNameByUID(fptr, (uint8_t *)&data[4 * EM4X50_DEVICE_ID], "-dump", 4);
267 pm3_save_dump(filename, data, EM4X50_DUMP_FILESIZE, jsfEM4x50);
268 return PM3_SUCCESS;
271 static int CmdEM4x50EView(const char *Cmd) {
273 CLIParserContext *ctx;
274 CLIParserInit(&ctx, "lf em 4x50 eview",
275 "Displays em4x50 content of emulator memory.",
276 "lf em 4x50 eview\n"
279 void *argtable[] = {
280 arg_param_begin,
281 arg_param_end
284 CLIExecWithReturn(ctx, Cmd, argtable, true);
285 CLIParserFree(ctx);
287 // download emulator memory
288 PrintAndLogEx(SUCCESS, "Reading emulator memory...");
289 uint8_t data[EM4X50_DUMP_FILESIZE] = {0x0};
290 if (GetFromDevice(BIG_BUF_EML, data, EM4X50_DUMP_FILESIZE, 0, NULL, 0, NULL, 2500, false) == false) {
291 PrintAndLogEx(WARNING, "Fail, transfer from device time-out");
292 return PM3_ETIMEOUT;
295 // valid em4x50 data?
296 uint32_t serial = bytes_to_num(data + 4 * EM4X50_DEVICE_SERIAL, 4);
297 uint32_t device_id = bytes_to_num(data + 4 * EM4X50_DEVICE_ID, 4);
298 if (serial == device_id) {
299 PrintAndLogEx(WARNING, "No valid em4x50 data in emulator memory.");
300 return PM3_ENODATA;
303 em4x50_word_t words[EM4X50_NO_WORDS];
304 for (int i = 0; i < EM4X50_NO_WORDS; i++) {
305 memcpy(words[i].byte, data + i * 4, 4);
307 em4x50_print_result(words, 0, EM4X50_NO_WORDS - 1);
308 PrintAndLogEx(NORMAL, "");
310 return PM3_SUCCESS;
313 static int CmdEM4x50Login(const char *Cmd) {
314 CLIParserContext *ctx;
315 CLIParserInit(&ctx, "lf em 4x50 login",
316 "Login into EM4x50 tag.",
317 "lf em 4x50 login -p 12345678 -> login with password 12345678\n"
320 void *argtable[] = {
321 arg_param_begin,
322 arg_str1("p", "passsword", "<hex>", "password, 4 bytes, lsb"),
323 arg_param_end
326 CLIExecWithReturn(ctx, Cmd, argtable, true);
327 int pwd_len = 0;
328 uint8_t pwd[4] = {0x0};
329 CLIGetHexWithReturn(ctx, 1, pwd, &pwd_len);
330 CLIParserFree(ctx);
332 if (pwd_len != 4) {
333 PrintAndLogEx(FAILED, "password length must be 4 bytes");
334 return PM3_EINVARG;
337 uint32_t password = BYTES2UINT32_BE(pwd);
339 // start
340 clearCommandBuffer();
341 PacketResponseNG resp;
342 SendCommandNG(CMD_LF_EM4X50_LOGIN, (uint8_t *)&password, sizeof(password));
343 WaitForResponse(CMD_LF_EM4X50_LOGIN, &resp);
345 // print response
346 if (resp.status == PM3_SUCCESS)
347 PrintAndLogEx(SUCCESS, "Login ( " _GREEN_("ok") " )");
348 else
349 PrintAndLogEx(FAILED, "Login ( " _RED_("failed") " )");
351 return resp.status;
354 static int CmdEM4x50Brute(const char *Cmd) {
355 CLIParserContext *ctx;
356 CLIParserInit(&ctx, "lf em 4x50 brute",
357 "Tries to bruteforce the password of a EM4x50 card.\n"
358 "Function can be stopped by pressing pm3 button.\n",
360 "lf em 4x50 brute --mode range --begin 12330000 --end 12340000 -> tries pwds from 0x12330000 to 0x12340000\n"
361 "lf em 4x50 brute --mode charset --digits --uppercase -> tries all combinations of ASCII codes for digits and uppercase letters\n"
362 "lf em 4x50 brute --mode smart -> enable 'smart' pattern key cracking\n"
365 void *argtable[] = {
366 arg_param_begin,
367 arg_str1(NULL, "mode", "<str>", "Bruteforce mode (range|charset|smart)"),
368 arg_str0(NULL, "begin", "<hex>", "Range mode - start of the key range"),
369 arg_str0(NULL, "end", "<hex>", "Range mode - end of the key range"),
370 arg_lit0(NULL, "digits", "Charset mode - include ASCII codes for digits"),
371 arg_lit0(NULL, "uppercase", "Charset mode - include ASCII codes for uppercase letters"),
372 arg_param_end
375 CLIExecWithReturn(ctx, Cmd, argtable, true);
377 em4x50_data_t etd;
378 memset(&etd, 0, sizeof(etd));
380 char mode[64];
381 int mode_len = sizeof(mode) - 1; // CLIGetStrWithReturn does not guarantee string to be null-terminated
382 CLIGetStrWithReturn(ctx, 1, (uint8_t *) mode, &mode_len);
383 PrintAndLogEx(INFO, "Chosen mode: %s", mode);
385 if (strcmp(mode, "range") == 0) {
386 etd.bruteforce_mode = BF_MODE_RANGE;
387 } else if (strcmp(mode, "charset") == 0) {
388 etd.bruteforce_mode = BF_MODE_CHARSET;
389 } else if (strcmp(mode, "smart") == 0) {
390 etd.bruteforce_mode = BF_MODE_SMART;
391 } else {
392 PrintAndLogEx(FAILED, "Unknown bruteforce mode: %s", mode);
393 CLIParserFree(ctx);
394 return PM3_EINVARG;
397 if (etd.bruteforce_mode == BF_MODE_RANGE) {
398 int begin_len = 0;
399 uint8_t begin[4] = {0x0};
400 CLIGetHexWithReturn(ctx, 2, begin, &begin_len);
402 int end_len = 0;
403 uint8_t end[4] = {0x0};
404 CLIGetHexWithReturn(ctx, 3, end, &end_len);
406 if (begin_len != 4) {
407 PrintAndLogEx(FAILED, "'begin' parameter must be 4 bytes");
408 CLIParserFree(ctx);
409 return PM3_EINVARG;
412 if (end_len != 4) {
413 PrintAndLogEx(FAILED, "'end' parameter must be 4 bytes");
414 CLIParserFree(ctx);
415 return PM3_EINVARG;
418 etd.password1 = BYTES2UINT32_BE(begin);
419 etd.password2 = BYTES2UINT32_BE(end);
420 } else if (etd.bruteforce_mode == BF_MODE_CHARSET) {
421 bool enable_digits = arg_get_lit(ctx, 4);
422 bool enable_uppercase = arg_get_lit(ctx, 5);
424 if (enable_digits)
425 etd.bruteforce_charset |= BF_CHARSET_DIGITS;
426 if (enable_uppercase)
427 etd.bruteforce_charset |= BF_CHARSET_UPPERCASE;
429 if (etd.bruteforce_charset == 0) {
430 PrintAndLogEx(FAILED, "Please enable at least one charset when using charset bruteforce mode.");
431 CLIParserFree(ctx);
432 return PM3_EINVARG;
435 PrintAndLogEx(INFO, "Enabled charsets: %s%s",
436 enable_digits ? "digits " : "",
437 enable_uppercase ? "uppercase " : "");
441 CLIParserFree(ctx);
443 // 27 passwords/second (empirical value)
444 const int speed = 27;
445 int no_iter = 0;
447 if (etd.bruteforce_mode == BF_MODE_RANGE) {
448 no_iter = etd.password2 - etd.password1 + 1;
449 PrintAndLogEx(INFO, "Trying " _YELLOW_("%i") " passwords in range [0x%08x, 0x%08x]"
450 , no_iter
451 , etd.password1
452 , etd.password2
454 } else if (etd.bruteforce_mode == BF_MODE_CHARSET) {
455 unsigned int digits = 0;
457 if (etd.bruteforce_charset & BF_CHARSET_DIGITS)
458 digits += BF_CHARSET_DIGITS_SIZE;
460 if (etd.bruteforce_charset & BF_CHARSET_UPPERCASE)
461 digits += BF_CHARSET_UPPERCASE_SIZE;
463 no_iter = pow(digits, 4);
466 // print some information
467 int dur_s = no_iter / speed;
468 int dur_h = dur_s / 3600;
469 int dur_m = (dur_s - dur_h * 3600) / 60;
471 dur_s -= dur_h * 3600 + dur_m * 60;
473 if (no_iter > 0)
474 PrintAndLogEx(INFO, "Estimated duration: %ih %im %is", dur_h, dur_m, dur_s);
475 else
476 PrintAndLogEx(INFO, "Estimated duration: unknown");
478 // start
479 clearCommandBuffer();
480 PacketResponseNG resp;
481 SendCommandNG(CMD_LF_EM4X50_BRUTE, (uint8_t *)&etd, sizeof(etd));
482 WaitForResponse(CMD_LF_EM4X50_BRUTE, &resp);
484 // print response
485 if (resp.status == PM3_SUCCESS)
486 PrintAndLogEx(SUCCESS, "found valid password [ " _GREEN_("%08"PRIX32) " ]", resp.data.asDwords[0]);
487 else
488 PrintAndLogEx(WARNING, "brute pwd failed");
490 return PM3_SUCCESS;
493 // upload passwords from given dictionary to device and start check;
494 // if no filename is given dictionary "t55xx_default_pwds.dic" is used
495 static int CmdEM4x50Chk(const char *Cmd) {
496 CLIParserContext *ctx;
497 CLIParserInit(&ctx, "lf em 4x50 chk",
498 "Run dictionary key recovery against EM4x50 card.",
499 "lf em 4x50 chk -> uses T55xx default dictionary\n"
500 "lf em 4x50 chk -f my.dic"
503 void *argtable[] = {
504 arg_param_begin,
505 arg_str0("f", "file", "<fn>", "specify dictionary filename"),
506 arg_param_end
509 CLIExecWithReturn(ctx, Cmd, argtable, true);
510 int fnlen = 0;
511 char filename[FILE_PATH_SIZE] = {0};
512 CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen);
513 CLIParserFree(ctx);
515 if (IfPm3Flash() == false) {
516 PrintAndLogEx(WARNING, "no flash memory available");
517 return PM3_EFLASH;
520 // no filename -> default = t55xx_default_pwds
521 if (strlen(filename) == 0) {
522 snprintf(filename, sizeof(filename), "t55xx_default_pwds");
523 PrintAndLogEx(INFO, "treating file as T55xx keys");
526 // load keys
527 uint8_t *keys = NULL;
528 uint32_t key_count = 0;
529 int res = loadFileDICTIONARY_safe(filename, (void **)&keys, 4, &key_count);
530 if (res != PM3_SUCCESS || key_count == 0) {
531 free(keys);
532 return res;
535 uint8_t *pkeys = keys;
537 uint64_t t1 = msclock();
539 PrintAndLogEx(INFO, "You can cancel this operation by pressing the pm3 button");
541 // block with 2000 bytes -> 500 keys
542 uint8_t destfn[32] = "em4x50_chk.bin";
543 PacketResponseNG resp;
544 int bytes_remaining = key_count * 4;
545 int status = PM3_EFAILED;
547 while (bytes_remaining > 0) {
549 PrintAndLogEx(INPLACE, "Remaining keys: %i ", bytes_remaining / 4);
551 // upload to flash.
552 size_t n = MIN(bytes_remaining, 2000);
553 res = flashmem_spiffs_load((char *)destfn, keys, n);
554 if (res != PM3_SUCCESS) {
555 PrintAndLogEx(WARNING, "SPIFFS upload failed");
556 return res;
559 clearCommandBuffer();
560 SendCommandNG(CMD_LF_EM4X50_CHK, destfn, sizeof(destfn));
561 WaitForResponseTimeoutW(CMD_LF_EM4X50_CHK, &resp, -1, false);
563 status = resp.status;
564 if ((status == PM3_SUCCESS) || (status == PM3_EOPABORTED))
565 break;
567 bytes_remaining -= n;
568 keys += n;
571 free(pkeys);
572 PrintAndLogEx(NORMAL, "");
574 if (status == PM3_SUCCESS) {
575 uint32_t pwd = BYTES2UINT32(resp.data.asBytes);
576 PrintAndLogEx(SUCCESS, "found valid password [ " _GREEN_("%08"PRIX32) " ]", pwd);
577 } else {
578 PrintAndLogEx(FAILED, "No key found");
581 t1 = msclock() - t1;
582 PrintAndLogEx(SUCCESS, "\ntime in check pwd " _YELLOW_("%.0f") " seconds\n", (float)t1 / 1000.0);
583 return PM3_SUCCESS;
586 //quick test for EM4x50 tag
587 bool detect_4x50_block(void) {
588 em4x50_data_t etd = {
589 .pwd_given = false,
590 .addr_given = true,
591 .addresses = (EM4X50_DEVICE_SERIAL << 8) | EM4X50_DEVICE_SERIAL,
593 em4x50_word_t words[EM4X50_NO_WORDS];
594 return (em4x50_read(&etd, words) == PM3_SUCCESS);
597 int read_em4x50_uid(void) {
598 em4x50_data_t etd = {
599 .pwd_given = false,
600 .addr_given = true,
601 .addresses = (EM4X50_DEVICE_SERIAL << 8) | EM4X50_DEVICE_SERIAL,
603 em4x50_word_t words[EM4X50_NO_WORDS];
604 int res = em4x50_read(&etd, words);
605 if (res == PM3_SUCCESS) {
606 PrintAndLogEx(INFO, " Serial: " _GREEN_("%s"), sprint_hex(words[EM4X50_DEVICE_SERIAL].byte, 4));
607 } else {
608 SendCommandNG(CMD_BREAK_LOOP, NULL, 0);
610 return res;
613 // envoke reading
614 // - with given address (option b) (and optional password if address is
615 // read protected) -> selective read mode
616 int em4x50_read(em4x50_data_t *etd, em4x50_word_t *out) {
618 em4x50_data_t edata = {
619 .pwd_given = false,
620 .addr_given = false,
623 if (etd != NULL) {
624 edata = *etd;
627 clearCommandBuffer();
628 SendCommandNG(CMD_LF_EM4X50_READ, (uint8_t *)&edata, sizeof(edata));
629 PacketResponseNG resp;
630 if (WaitForResponseTimeout(CMD_LF_EM4X50_READ, &resp, EM4X50_TIMEOUT_CMD) == false) {
631 PrintAndLogEx(WARNING, "(em4x50) timeout while waiting for reply.");
632 return PM3_ETIMEOUT;
635 if (resp.status != PM3_SUCCESS) {
636 return PM3_ESOFT;
639 em4x50_read_data_response_t *o = (em4x50_read_data_response_t *)resp.data.asBytes;
641 em4x50_word_t words[EM4X50_NO_WORDS] = {0};
642 em4x50_prepare_result((uint8_t *)o->words, etd->addresses & 0xFF, (etd->addresses >> 8) & 0xFF, words);
644 if (out != NULL) {
645 memcpy(out, &words, sizeof(em4x50_word_t) * EM4X50_NO_WORDS);
648 em4x50_print_result(words, etd->addresses & 0xFF, (etd->addresses >> 8) & 0xFF);
649 return PM3_SUCCESS;
652 static int CmdEM4x50Read(const char *Cmd) {
653 CLIParserContext *ctx;
654 CLIParserInit(&ctx, "lf em 4x50 rdbl",
655 "Reads single EM4x50 block/word.",
656 "lf em 4x50 rdbl -b 3\n"
657 "lf em 4x50 rdbl -b 32 -p 12345678 -> reads block 32 with pwd 0x12345678\n"
660 void *argtable[] = {
661 arg_param_begin,
662 arg_int1("b", "block", "<dec>", "block/word address"),
663 arg_str0("p", "pwd", "<hex>", "password, 4 hex bytes, lsb"),
664 arg_param_end
667 CLIExecWithReturn(ctx, Cmd, argtable, true);
669 int addr = arg_get_int_def(ctx, 1, 0);
670 int pwd_len = 0;
671 uint8_t pwd[4] = {0x0};
672 CLIGetHexWithReturn(ctx, 2, pwd, &pwd_len);
673 CLIParserFree(ctx);
675 if (addr <= 0 || addr >= EM4X50_NO_WORDS) {
676 return PM3_EINVARG;
679 em4x50_data_t etd;
681 // init
682 memset(&etd, 0x00, sizeof(em4x50_data_t));
683 etd.pwd_given = false;
684 etd.addresses = (addr << 8) | addr;
685 etd.addr_given = true;
687 if (pwd_len) {
688 if (pwd_len != 4) {
689 PrintAndLogEx(FAILED, "password length must be 4 bytes instead of %d", pwd_len);
690 return PM3_EINVARG;
691 } else {
692 etd.password1 = BYTES2UINT32_BE(pwd);
693 etd.pwd_given = true;
697 return em4x50_read(&etd, NULL);
700 // envoke reading of a EM4x50 tag which has to be on the antenna because
701 // decoding is done by the device (not on client side)
702 static int CmdEM4x50Info(const char *Cmd) {
703 CLIParserContext *ctx;
704 CLIParserInit(&ctx, "lf em 4x50 info",
705 "Tag information EM4x50.",
706 "lf em 4x50 info\n"
707 "lf em 4x50 info -v -> show data section\n"
708 "lf em 4x50 info -p 12345678 -> uses pwd 0x12345678\n"
711 void *argtable[] = {
712 arg_param_begin,
713 arg_str0("p", "pwd", "<hex>", "password, 4 hex bytes, lsb"),
714 arg_lit0("v", "verbose", "verbose output"),
715 arg_param_end
718 CLIExecWithReturn(ctx, Cmd, argtable, true);
719 int pwd_len = 0;
720 uint8_t pwd[4] = {0x0};
721 CLIGetHexWithReturn(ctx, 1, pwd, &pwd_len);
722 bool verb = arg_get_lit(ctx, 2);
723 CLIParserFree(ctx);
725 em4x50_data_t etd = {.pwd_given = false};
726 if (pwd_len) {
727 if (pwd_len != 4) {
728 PrintAndLogEx(FAILED, "password length must be 4 bytes instead of %d", pwd_len);
729 return PM3_EINVARG;
730 } else {
731 etd.password1 = BYTES2UINT32_BE(pwd);
732 etd.pwd_given = true;
736 clearCommandBuffer();
737 SendCommandNG(CMD_LF_EM4X50_INFO, (uint8_t *)&etd, sizeof(etd));
738 PacketResponseNG resp;
739 if (WaitForResponseTimeout(CMD_LF_EM4X50_INFO, &resp, EM4X50_TIMEOUT_CMD) == false) {
740 PrintAndLogEx(WARNING, "Timeout while waiting for reply.");
741 return PM3_ETIMEOUT;
744 if (resp.status == PM3_SUCCESS)
745 em4x50_print_info_result(resp.data.asBytes, verb);
746 else
747 PrintAndLogEx(FAILED, "Reading tag " _RED_("failed"));
749 return resp.status;
752 static int CmdEM4x50Reader(const char *Cmd) {
753 CLIParserContext *ctx;
754 CLIParserInit(&ctx, "lf em 4x50 reader",
755 "Shows standard read data of EM4x50 tag.",
756 "lf em 4x50 reader\n"
757 "lf em 4x50 reader -@ -> continuous reader mode"
760 void *argtable[] = {
761 arg_param_begin,
762 arg_lit0("@", NULL, "optional - continuous reader mode"),
763 arg_param_end
766 CLIExecWithReturn(ctx, Cmd, argtable, true);
767 bool cm = arg_get_lit(ctx, 1);
768 CLIParserFree(ctx);
770 // start
771 do {
773 PacketResponseNG resp;
774 clearCommandBuffer();
775 SendCommandNG(CMD_LF_EM4X50_READER, 0, 0);
776 WaitForResponseTimeoutW(CMD_LF_EM4X50_READER, &resp, -1, false);
778 // iceman, misuse of return status code.
779 int now = resp.status;
781 if (now > 0) {
783 em4x50_word_t words[EM4X50_NO_WORDS];
784 em4x50_prepare_result(resp.data.asBytes, 0, now - 1, words);
786 PrintAndLogEx(NORMAL, "");
787 PrintAndLogEx(INFO, " word (msb) | word (lsb) ");
788 PrintAndLogEx(INFO, "-------------+-------------");
790 for (int i = 0; i < now; i++) {
792 char r[30];
793 memset(r, 0, sizeof(r));
794 for (int j = 3; j >= 0; j--) {
795 int offset = strlen(r);
796 snprintf(r + offset, sizeof(r) - offset, "%02x ", reflect8(words[i].byte[j]));
799 PrintAndLogEx(INFO, _GREEN_(" %s") "| %s", sprint_hex(words[i].byte, 4), r);
802 PrintAndLogEx(INFO, "-------------+-------------");
804 } while (cm && (kbd_enter_pressed() == false));
806 return PM3_SUCCESS;
809 static int CmdEM4x50Dump(const char *Cmd) {
810 CLIParserContext *ctx;
811 CLIParserInit(&ctx, "lf em 4x50 dump",
812 "Reads all blocks/words from EM4x50 tag and saves dump in (bin/json) format",
813 "lf em 4x50 dump\n"
814 "lf em 4x50 dump -f mydump\n"
815 "lf em 4x50 dump -p 12345678\n"
816 "lf em 4x50 dump -f mydump -p 12345678"
819 void *argtable[] = {
820 arg_param_begin,
821 arg_str0("f", "file", "<fn>", "specify dump filename"),
822 arg_str0("p", "pwd", "<hex>", "password, 4 hex bytes, lsb"),
823 arg_lit0(NULL, "ns", "no save to file"),
824 arg_param_end
827 CLIExecWithReturn(ctx, Cmd, argtable, true);
828 int fnLen = 0;
829 char filename[FILE_PATH_SIZE] = {0};
830 CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnLen);
832 int pwd_len = 0;
833 uint8_t pwd[4] = {0x0};
834 CLIGetHexWithReturn(ctx, 2, pwd, &pwd_len);
836 bool nosave = arg_get_lit(ctx, 3);
837 CLIParserFree(ctx);
839 em4x50_data_t etd = {.pwd_given = false};
841 if (pwd_len) {
842 if (pwd_len != 4) {
843 PrintAndLogEx(FAILED, "password length must be 4 bytes");
844 CLIParserFree(ctx);
845 return PM3_EINVARG;
846 } else {
847 etd.password1 = BYTES2UINT32_BE(pwd);
848 etd.pwd_given = true;
852 PrintAndLogEx(INFO, "Reading EM4x50 tag");
853 clearCommandBuffer();
854 SendCommandNG(CMD_LF_EM4X50_INFO, (uint8_t *)&etd, sizeof(etd));
855 PacketResponseNG resp;
856 if (WaitForResponseTimeout(CMD_LF_EM4X50_INFO, &resp, EM4X50_TIMEOUT_CMD) == false) {
857 PrintAndLogEx(WARNING, "Timeout while waiting for reply");
858 return PM3_ETIMEOUT;
861 if (resp.status != PM3_SUCCESS) {
862 PrintAndLogEx(FAILED, "Reading tag ( " _RED_("failed") " )");
863 return PM3_ESOFT;
866 // structured format
867 em4x50_word_t words[EM4X50_NO_WORDS];
868 em4x50_prepare_result(resp.data.asBytes, 0, EM4X50_NO_WORDS - 1, words);
870 // result output
871 PrintAndLogEx(INFO, _YELLOW_("EM4x50 data:"));
872 em4x50_print_result(words, 0, EM4X50_NO_WORDS - 1);
874 if (nosave) {
875 PrintAndLogEx(NORMAL, "");
876 PrintAndLogEx(INFO, "Called with no save option");
877 PrintAndLogEx(NORMAL, "");
878 return PM3_SUCCESS;
881 // user supplied filename?
882 if (fnLen == 0) {
883 PrintAndLogEx(INFO, "Using UID as filename");
884 char *fptr = filename + snprintf(filename, sizeof(filename), "lf-4x50-");
885 FillFileNameByUID(fptr, words[EM4X50_DEVICE_ID].byte, "-dump", 4);
888 uint8_t data[EM4X50_DUMP_FILESIZE] = {0};
889 for (int i = 0; i < EM4X50_NO_WORDS; i++) {
890 memcpy(data + (i * 4), words[i].byte, 4);
893 pm3_save_dump(filename, data, sizeof(data), jsfEM4x50);
894 return PM3_SUCCESS;
897 // envoke writing a single word (32 bit) to a EM4x50 tag
898 static int CmdEM4x50Write(const char *Cmd) {
899 CLIParserContext *ctx;
900 CLIParserInit(&ctx, "lf em 4x50 wrbl",
901 "Writes single block/word to EM4x50 tag.",
902 "lf em 4x50 wrbl -b 3 -d 4f22e7ff \n"
903 "lf em 4x50 wrbl -b 3 -d 4f22e7ff -p 12345678\n"
906 void *argtable[] = {
907 arg_param_begin,
908 arg_int1("b", "block", "<dec>", "block/word address, dec"),
909 arg_str1("d", "data", "<hex>", "data, 4 bytes, lsb"),
910 arg_str0("p", "pwd", "<hex>", "password, 4 bytes, lsb"),
911 arg_param_end
914 CLIExecWithReturn(ctx, Cmd, argtable, true);
916 int addr = arg_get_int_def(ctx, 1, 0);
918 int word_len = 0;
919 uint8_t word[4] = {0x0};
920 CLIGetHexWithReturn(ctx, 2, word, &word_len);
922 int pwd_len = 0;
923 uint8_t pwd[4] = {0x0};
924 CLIGetHexWithReturn(ctx, 3, pwd, &pwd_len);
925 CLIParserFree(ctx);
927 if (addr <= 0 || addr >= EM4X50_NO_WORDS) {
928 PrintAndLogEx(FAILED, "address has to be within range [0, 31]");
929 return PM3_EINVARG;
932 if (word_len != 4) {
933 PrintAndLogEx(FAILED, "word/data length must be 4 bytes instead of %d", word_len);
934 return PM3_EINVARG;
937 em4x50_data_t etd = {.pwd_given = false};
938 if (pwd_len) {
939 if (pwd_len != 4) {
940 PrintAndLogEx(FAILED, "password length must be 4 bytes instead of %d", pwd_len);
941 return PM3_EINVARG;
942 } else {
943 etd.password1 = BYTES2UINT32_BE(pwd);
944 etd.pwd_given = true;
948 etd.addresses = (addr << 8) | addr;
949 etd.addr_given = true;
950 etd.word = BYTES2UINT32_BE(word);
952 clearCommandBuffer();
953 SendCommandNG(CMD_LF_EM4X50_WRITE, (uint8_t *)&etd, sizeof(etd));
954 PacketResponseNG resp;
955 if (WaitForResponseTimeout(CMD_LF_EM4X50_WRITE, &resp, EM4X50_TIMEOUT_CMD) == false) {
956 PrintAndLogEx(WARNING, "Timeout while waiting for reply.");
957 return PM3_ETIMEOUT;
960 int status = resp.status;
961 if (status == PM3_ETEAROFF) {
962 return status;
965 if (status != PM3_SUCCESS) {
966 PrintAndLogEx(FAILED, "Writing ( " _RED_("failed") " )");
967 return PM3_ESOFT;
970 // display result of writing operation in structured format
971 uint8_t *data = resp.data.asBytes;
972 em4x50_word_t words[EM4X50_NO_WORDS];
974 em4x50_prepare_result(data, addr, addr, words);
975 em4x50_print_result(words, addr, addr);
976 PrintAndLogEx(SUCCESS, "Write ( " _GREEN_("ok") " )");
977 PrintAndLogEx(HINT, "Try `" _YELLOW_("lf em 4x50 rdbl -b %u") "` - to read your data", addr);
978 PrintAndLogEx(INFO, "Done!");
979 return PM3_SUCCESS;
982 // envokes changing the password of EM4x50 tag
983 static int CmdEM4x50WritePwd(const char *Cmd) {
984 CLIParserContext *ctx;
985 CLIParserInit(&ctx, "lf em 4x50 wrpwd",
986 "Writes EM4x50 password.",
987 "lf em 4x50 wrpwd -p 4f22e7ff -n 12345678"
990 void *argtable[] = {
991 arg_param_begin,
992 arg_str1("p", "pwd", "<hex>", "password, 4 hex bytes, lsb"),
993 arg_str1("n", "new", "<hex>", "new password, 4 hex bytes, lsb"),
994 arg_param_end
997 CLIExecWithReturn(ctx, Cmd, argtable, true);
998 int pwd_len = 0;
999 uint8_t pwd[4] = {0x0};
1000 CLIGetHexWithReturn(ctx, 1, pwd, &pwd_len);
1002 int npwd_len = 0;
1003 uint8_t npwd[4] = {0x0};
1004 CLIGetHexWithReturn(ctx, 2, npwd, &npwd_len);
1006 CLIParserFree(ctx);
1008 em4x50_data_t etd;
1009 if (pwd_len != 4) {
1010 PrintAndLogEx(FAILED, "password length must be 4 bytes instead of %d", pwd_len);
1011 return PM3_EINVARG;
1012 } else {
1013 etd.password1 = BYTES2UINT32_BE(pwd);
1016 if (npwd_len != 4) {
1017 PrintAndLogEx(FAILED, "password length must be 4 bytes instead of %d", npwd_len);
1018 return PM3_EINVARG;
1019 } else {
1020 etd.password2 = BYTES2UINT32_BE(npwd);
1023 PacketResponseNG resp;
1024 clearCommandBuffer();
1025 SendCommandNG(CMD_LF_EM4X50_WRITEPWD, (uint8_t *)&etd, sizeof(etd));
1026 if (WaitForResponseTimeout(CMD_LF_EM4X50_WRITEPWD, &resp, EM4X50_TIMEOUT_CMD) == false) {
1027 PrintAndLogEx(WARNING, "Timeout while waiting for reply.");
1028 return PM3_ETIMEOUT;
1031 if (resp.status == PM3_ETEAROFF) {
1032 PrintAndLogEx(INFO, "Tear off triggered");
1033 return PM3_SUCCESS;
1036 if (resp.status != PM3_SUCCESS) {
1037 PrintAndLogEx(FAILED, "Writing password ( " _RED_("fail") " )");
1038 return PM3_EFAILED;
1041 PrintAndLogEx(SUCCESS, "Writing new password %s ( %s )"
1042 , sprint_hex_inrow(npwd, sizeof(npwd))
1043 , _GREEN_("ok")
1045 return PM3_SUCCESS;
1048 // fills EM4x50 tag with zeros including password
1049 static int CmdEM4x50Wipe(const char *Cmd) {
1050 CLIParserContext *ctx;
1051 CLIParserInit(&ctx, "lf em 4x50 wipe",
1052 "Wipes EM4x50 tag by filling it with zeros, including the new password\n"
1053 "Must give a password.",
1054 "lf em 4x50 wipe -p 12345678"
1057 void *argtable[] = {
1058 arg_param_begin,
1059 arg_str1("p", "passsword", "<hex>", "password, 4 bytes, lsb"),
1060 arg_param_end
1063 CLIExecWithReturn(ctx, Cmd, argtable, true);
1064 int pwd_len = 0;
1065 uint8_t pwd[4] = {0x0};
1066 CLIGetHexWithReturn(ctx, 1, pwd, &pwd_len);
1067 CLIParserFree(ctx);
1069 if (pwd_len != 4) {
1070 PrintAndLogEx(FAILED, "password length must be 4 bytes instead of %d", pwd_len);
1071 CLIParserFree(ctx);
1072 return PM3_EINVARG;
1075 em4x50_data_t etd = {.pwd_given = false, .word = 0x0, .password2 = 0x0};
1077 etd.password1 = BYTES2UINT32_BE(pwd);
1078 etd.pwd_given = true;
1080 // clear password
1081 PacketResponseNG resp;
1082 clearCommandBuffer();
1083 SendCommandNG(CMD_LF_EM4X50_WRITEPWD, (uint8_t *)&etd, sizeof(etd));
1084 if (WaitForResponseTimeout(CMD_LF_EM4X50_WRITEPWD, &resp, EM4X50_TIMEOUT_CMD) == false) {
1085 PrintAndLogEx(WARNING, "Timeout while waiting for reply");
1086 return PM3_ETIMEOUT;
1089 if (resp.status == PM3_SUCCESS) {
1090 PrintAndLogEx(SUCCESS, "Resetting password to 00000000 ( " _GREEN_("ok") " )");
1091 } else {
1092 PrintAndLogEx(FAILED, "Resetting password ( " _RED_("failed") " )");
1093 return PM3_ESOFT;
1096 // from now on new password 0x0
1097 etd.password1 = 0x0;
1099 // clear data (words 1 to 31)
1100 for (int i = 1; i < EM4X50_DEVICE_SERIAL; i++) {
1102 // no login necessary for blocks 3 to 31
1103 etd.pwd_given = (i <= EM4X50_CONTROL);
1105 PrintAndLogEx(INPLACE, "Wiping block %i", i);
1107 etd.addresses = i << 8 | i;
1108 clearCommandBuffer();
1109 SendCommandNG(CMD_LF_EM4X50_WRITE, (uint8_t *)&etd, sizeof(etd));
1110 if (WaitForResponseTimeout(CMD_LF_EM4X50_WRITE, &resp, EM4X50_TIMEOUT_CMD) == false) {
1111 PrintAndLogEx(WARNING, "Timeout while waiting for reply.");
1112 return PM3_ETIMEOUT;
1115 if (resp.status != PM3_SUCCESS) {
1116 PrintAndLogEx(NORMAL, "");
1117 PrintAndLogEx(FAILED, "Wiping data " _RED_("failed"));
1118 return PM3_ESOFT;
1122 PrintAndLogEx(NORMAL, "");
1123 PrintAndLogEx(INFO, "Done!");
1124 return PM3_SUCCESS;
1127 static int CmdEM4x50Restore(const char *Cmd) {
1128 CLIParserContext *ctx;
1129 CLIParserInit(&ctx, "lf em 4x50 restore",
1130 "Restores data from dumpfile (bin/eml/json) onto a EM4x50 tag.\n"
1131 "if used with -u, the filetemplate `lf-4x50-UID-dump.bin` is used as filename",
1132 "lf em 4x50 restore -u 1b5aff5c -> uses lf-4x50-1B5AFF5C-dump.bin\n"
1133 "lf em 4x50 restore -f mydump.eml\n"
1134 "lf em 4x50 restore -u 1b5aff5c -p 12345678\n"
1135 "lf em 4x50 restore -f mydump.eml -p 12345678\n"
1138 void *argtable[] = {
1139 arg_param_begin,
1140 arg_str0("u", "uid", "<hex>", "uid, 4 hex bytes, msb"),
1141 arg_str0("f", "file", "<fn>", "specify a filename for dump file"),
1142 arg_str0("p", "pwd", "<hex>", "password, 4 hex bytes, lsb"),
1143 arg_param_end
1146 CLIExecWithReturn(ctx, Cmd, argtable, true);
1148 int uidLen = 0;
1149 uint8_t uid[4] = {0x0};
1150 CLIGetHexWithReturn(ctx, 1, uid, &uidLen);
1151 int fnlen = 0;
1152 char filename[FILE_PATH_SIZE] = {0};
1153 CLIParamStrToBuf(arg_get_str(ctx, 2), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen);
1155 int pwd_len = 0;
1156 uint8_t pwd[4] = {0x0};
1157 CLIGetHexWithReturn(ctx, 3, pwd, &pwd_len);
1158 CLIParserFree(ctx);
1160 if ((uidLen && fnlen) || (!uidLen && !fnlen)) {
1161 PrintAndLogEx(FAILED, "either use option 'u' or option 'f'");
1162 return PM3_EINVARG;
1165 int startblock = EM4X50_CONTROL + 1;
1166 em4x50_data_t etd = {.pwd_given = false};
1168 if (pwd_len) {
1169 if (pwd_len != 4) {
1170 PrintAndLogEx(FAILED, "password length must be 4 bytes instead of %d", pwd_len);
1171 return PM3_EINVARG;
1172 } else {
1173 etd.password1 = BYTES2UINT32_BE(pwd);
1174 etd.pwd_given = true;
1175 // if password is available protection and control word can be restored
1176 startblock = EM4X50_PROTECTION;
1180 if (uidLen) {
1181 PrintAndLogEx(INFO, "Using UID as filename");
1182 char *fptr = filename + snprintf(filename, sizeof(filename), "lf-4x50-");
1183 FillFileNameByUID(fptr, uid, "-dump", 4);
1186 PrintAndLogEx(INFO, "Restoring " _YELLOW_("%s")" to card", filename);
1188 // read data from dump file, also verify if dump is valid
1189 uint8_t data[EM4X50_DUMP_FILESIZE] = {0x0};
1190 size_t bytes_read = 0;
1191 if (em4x50_load_file(filename, data, EM4X50_DUMP_FILESIZE, &bytes_read) != PM3_SUCCESS) {
1192 return PM3_EFILE;
1195 for (int i = startblock; i < EM4X50_DEVICE_SERIAL; i++) {
1197 PrintAndLogEx(INPLACE, "Restoring block %i", i);
1199 etd.addresses = i << 8 | i;
1200 etd.word = reflect32(BYTES2UINT32_BE((data + 4 * i)));
1202 PacketResponseNG resp;
1203 clearCommandBuffer();
1204 SendCommandNG(CMD_LF_EM4X50_WRITE, (uint8_t *)&etd, sizeof(etd));
1205 if (WaitForResponseTimeout(CMD_LF_EM4X50_WRITE, &resp, EM4X50_TIMEOUT_CMD) == false) {
1206 PrintAndLogEx(NORMAL, "");
1207 PrintAndLogEx(WARNING, "Timeout while waiting for reply.");
1208 return PM3_ETIMEOUT;
1211 if (resp.status != PM3_SUCCESS) {
1212 PrintAndLogEx(NORMAL, "");
1213 PrintAndLogEx(FAILED, "Restoring data " _RED_("failed"));
1214 return PM3_ESOFT;
1218 PrintAndLogEx(NORMAL, "");
1219 PrintAndLogEx(INFO, "Done!");
1220 return PM3_SUCCESS;
1223 static int CmdEM4x50Sim(const char *Cmd) {
1224 CLIParserContext *ctx;
1225 CLIParserInit(&ctx, "lf em 4x50 sim",
1226 "Simulates a EM4x50 tag\n"
1227 "First upload to device using `lf em 4x50 eload`",
1228 "lf em 4x50 sim\n"
1229 "lf em 4x50 sim -p 27182818 -> uses password for eload data"
1232 void *argtable[] = {
1233 arg_param_begin,
1234 arg_str0("p", "passsword", "<hex>", "password, 4 bytes, lsb"),
1235 arg_param_end
1238 CLIExecWithReturn(ctx, Cmd, argtable, true);
1239 int pwd_len = 0;
1240 uint8_t pwd[4] = {0};
1241 CLIGetHexWithReturn(ctx, 1, pwd, &pwd_len);
1242 CLIParserFree(ctx);
1244 uint32_t password = 0;
1245 if (pwd_len) {
1246 if (pwd_len != 4) {
1247 PrintAndLogEx(FAILED, "password length must be 4 bytes, got %d", pwd_len);
1248 return PM3_EINVARG;
1249 } else {
1250 password = BYTES2UINT32_BE(pwd);
1254 int status = PM3_EFAILED;
1255 PrintAndLogEx(INFO, "Starting simulating");
1257 clearCommandBuffer();
1258 SendCommandNG(CMD_LF_EM4X50_SIM, (uint8_t *)&password, sizeof(password));
1260 PrintAndLogEx(INFO, "Press " _GREEN_("pm3 button") " or " _GREEN_("<Enter>") " to abort simulation");
1262 PacketResponseNG resp;
1263 // init to ZERO
1264 resp.cmd = 0,
1265 resp.length = 0,
1266 resp.magic = 0,
1267 resp.status = 0,
1268 resp.crc = 0,
1269 resp.ng = false,
1270 resp.oldarg[0] = 0;
1271 resp.oldarg[1] = 0;
1272 resp.oldarg[2] = 0;
1273 memset(resp.data.asBytes, 0, PM3_CMD_DATA_SIZE);
1275 bool keypress;
1276 do {
1277 keypress = kbd_enter_pressed();
1279 if (WaitForResponseTimeout(CMD_LF_EM4X50_SIM, &resp, 1500)) {
1280 status = resp.status;
1281 break;
1284 } while (keypress == false);
1286 if (keypress) {
1287 SendCommandNG(CMD_BREAK_LOOP, NULL, 0);
1288 status = PM3_EOPABORTED;
1291 if ((status == PM3_SUCCESS) || (status == PM3_EOPABORTED))
1292 PrintAndLogEx(INFO, "Done!");
1293 else
1294 PrintAndLogEx(FAILED, "No valid EM4x50 data in memory");
1296 return resp.status;
1299 static int CmdEM4x50View(const char *Cmd) {
1301 CLIParserContext *ctx;
1302 CLIParserInit(&ctx, "lf em 4x50 view",
1303 "Print a EM4x50 dump file\n",
1304 "lf em 4x50 view -f lf-4x50-01020304-dump.json"
1306 void *argtable[] = {
1307 arg_param_begin,
1308 arg_str0("f", "file", "<fn>", "specify a filename for dump file"),
1309 arg_param_end
1311 CLIExecWithReturn(ctx, Cmd, argtable, true);
1313 int fnlen = 0;
1314 char filename[FILE_PATH_SIZE] = {0};
1315 CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen);
1317 CLIParserFree(ctx);
1319 // read data from dump file, also verify if dump is valid
1320 uint8_t data[EM4X50_DUMP_FILESIZE] = {0x0};
1321 size_t bytes_read = 0;
1322 if (em4x50_load_file(filename, data, EM4X50_DUMP_FILESIZE, &bytes_read) != PM3_SUCCESS) {
1323 return PM3_EFILE;
1326 em4x50_word_t words[EM4X50_NO_WORDS];
1327 for (int i = 0; i < EM4X50_NO_WORDS; i++) {
1328 memcpy(words[i].byte, data + i * 4, 4);
1331 // result output
1332 em4x50_print_result(words, 0, EM4X50_NO_WORDS - 1);
1333 PrintAndLogEx(NORMAL, "");
1334 return PM3_SUCCESS;
1337 static command_t CommandTable[] = {
1338 {"help", CmdHelp, AlwaysAvailable, "This help"},
1339 {"-----------", CmdHelp, AlwaysAvailable, "--------------------- " _CYAN_("operations") " ---------------------"},
1340 {"brute", CmdEM4x50Brute, IfPm3EM4x50, "Bruteforce attack to find password"},
1341 {"chk", CmdEM4x50Chk, IfPm3EM4x50, "Check passwords"},
1342 {"dump", CmdEM4x50Dump, IfPm3EM4x50, "Dump EM4x50 tag"},
1343 {"info", CmdEM4x50Info, IfPm3EM4x50, "Tag information"},
1344 {"login", CmdEM4x50Login, IfPm3EM4x50, "Login into EM4x50 tag"},
1345 {"rdbl", CmdEM4x50Read, IfPm3EM4x50, "Read EM4x50 word data"},
1346 {"reader", CmdEM4x50Reader, IfPm3EM4x50, "Show standard read mode data"},
1347 {"restore", CmdEM4x50Restore, IfPm3EM4x50, "Restore EM4x50 dump to tag"},
1348 {"view", CmdEM4x50View, AlwaysAvailable, "Display content from tag dump file"},
1349 {"wipe", CmdEM4x50Wipe, IfPm3EM4x50, "Wipe EM4x50 tag"},
1350 {"wrbl", CmdEM4x50Write, IfPm3EM4x50, "Write EM4x50 word data"},
1351 {"wrpwd", CmdEM4x50WritePwd, IfPm3EM4x50, "Change EM4x50 password"},
1352 {"-----------", CmdHelp, AlwaysAvailable, "--------------------- " _CYAN_("simulation") " ---------------------"},
1353 {"eload", CmdEM4x50ELoad, IfPm3EM4x50, "Upload file into emulator memory"},
1354 {"esave", CmdEM4x50ESave, IfPm3EM4x50, "Save emulator memory to file"},
1355 {"eview", CmdEM4x50EView, IfPm3EM4x50, "View emulator memory"},
1356 {"sim", CmdEM4x50Sim, IfPm3EM4x50, "Simulate EM4x50 tag"},
1357 {NULL, NULL, NULL, NULL}
1360 static int CmdHelp(const char *Cmd) {
1361 (void)Cmd; // Cmd is not used so far
1362 CmdsHelp(CommandTable);
1363 return PM3_SUCCESS;
1366 int CmdLFEM4X50(const char *Cmd) {
1367 clearCommandBuffer();
1368 return CmdsParse(CommandTable, Cmd);