proxmark3/client/src/cmdlfem4x50.c

1241 lines
40 KiB
C
Raw Normal View History

2020-06-15 20:31:34 +08:00
//-----------------------------------------------------------------------------
// Copyright (C) 2020 tharexde
//
2020-12-04 07:11:57 +08:00
// modified iceman, 2020
//
2020-06-15 20:31:34 +08:00
// 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.
//-----------------------------------------------------------------------------
// Low frequency EM4x50 commands
//-----------------------------------------------------------------------------
#include "cliparser.h"
2020-06-15 20:31:34 +08:00
#include "cmdlfem4x50.h"
#include <ctype.h>
2020-12-04 07:11:57 +08:00
#include "cmdparser.h" // command_t
2021-06-25 16:02:30 +08:00
#include "util_posix.h" // msclock
2020-06-15 20:31:34 +08:00
#include "fileutils.h"
#include "commonutil.h"
#include "pmflash.h"
#include "cmdflashmemspiffs.h"
2021-05-17 05:03:13 +08:00
#include "em4x50.h"
static int CmdHelp(const char *Cmd);
2020-06-15 20:31:34 +08:00
2020-10-12 02:06:03 +08:00
static void prepare_result(const uint8_t *data, int fwr, int lwr, em4x50_word_t *words) {
2020-06-15 20:31:34 +08:00
2020-10-12 02:06:03 +08:00
// restructure received result in "em4x50_word_t" structure
2020-12-09 19:18:01 +08:00
for (int i = fwr; i <= lwr; i++) {
for (int j = 0; j < 4; j++) {
2020-12-12 21:46:40 +08:00
words[i].byte[j] = data[i * 4 + (3 - j)];
2020-12-09 19:18:01 +08:00
}
}
2020-06-15 20:31:34 +08:00
}
static void print_result(const em4x50_word_t *words, int fwr, int lwr) {
2020-07-01 00:50:38 +08:00
2020-06-15 20:31:34 +08:00
// print available information for given word from fwr to lwr, i.e.
// bit table + summary lines with hex notation of word (msb + lsb)
2020-07-01 00:50:38 +08:00
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, " # | word (msb) | word (lsb) | desc");
PrintAndLogEx(INFO, "----+-------------+-------------+--------------------");
2020-06-15 20:31:34 +08:00
for (int i = fwr; i <= lwr; i++) {
char s[50] = {0};
2020-08-13 18:25:04 +08:00
switch (i) {
case EM4X50_DEVICE_PASSWORD:
sprintf(s, _YELLOW_("password, write only"));
break;
case EM4X50_PROTECTION:
sprintf(s, _YELLOW_("protection cfg (locked)"));
break;
case EM4X50_CONTROL:
sprintf(s, _YELLOW_("control cfg (locked)"));
break;
case EM4X50_DEVICE_SERIAL:
sprintf(s, _YELLOW_("device serial number (read only)"));
break;
case EM4X50_DEVICE_ID:
sprintf(s, _YELLOW_("device identification (read only)"));
break;
default:
sprintf(s, "user data");
break;
}
char r[30] = {0};
for (int j = 3; j >= 0; j--) {
sprintf(r + strlen(r), "%02x ", reflect8(words[i].byte[j]));
2020-06-15 20:31:34 +08:00
}
2020-08-13 18:25:04 +08:00
PrintAndLogEx(INFO, " %2i | " _GREEN_("%s") "| %s| %s",
2020-08-13 18:25:04 +08:00
i,
sprint_hex(words[i].byte, 4),
r,
s
);
2020-06-15 20:31:34 +08:00
}
PrintAndLogEx(INFO, "----+-------------+-------------+--------------------");
2020-06-15 20:31:34 +08:00
}
static void print_info_result(uint8_t *data, bool verbose) {
2020-06-15 20:31:34 +08:00
// display all information of info result in structured format
em4x50_word_t words[EM4X50_NO_WORDS];
prepare_result(data, 0, EM4X50_NO_WORDS - 1, words);
bool bpwc = words[EM4X50_CONTROL].byte[CONFIG_BLOCK] & PASSWORD_CHECK;
bool braw = words[EM4X50_CONTROL].byte[CONFIG_BLOCK] & READ_AFTER_WRITE;
int fwr = reflect8(words[EM4X50_CONTROL].byte[FIRST_WORD_READ]);
int lwr = reflect8(words[EM4X50_CONTROL].byte[LAST_WORD_READ]);
int fwrp = reflect8(words[EM4X50_PROTECTION].byte[FIRST_WORD_READ_PROTECTED]);
int lwrp = reflect8(words[EM4X50_PROTECTION].byte[LAST_WORD_READ_PROTECTED]);
int fwwi = reflect8(words[EM4X50_PROTECTION].byte[FIRST_WORD_WRITE_INHIBITED]);
int lwwi = reflect8(words[EM4X50_PROTECTION].byte[LAST_WORD_WRITE_INHIBITED]);
2020-07-01 00:50:38 +08:00
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, "--- " _CYAN_("Tag Information") " ---------------------------");
2020-07-01 00:50:38 +08:00
2020-06-15 20:31:34 +08:00
// data section
if (verbose) {
print_result(words, 0, EM4X50_NO_WORDS - 1);
} else {
print_result(words, EM4X50_DEVICE_SERIAL, EM4X50_DEVICE_ID);
}
2020-08-13 18:25:04 +08:00
2020-06-15 20:31:34 +08:00
// configuration section
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, "---- " _CYAN_("Configuration") " ----");
2020-06-15 20:31:34 +08:00
2021-06-25 01:27:12 +08:00
PrintAndLogEx(INFO, "first word read.... " _YELLOW_("%i"), fwr);
PrintAndLogEx(INFO, "last word read..... " _YELLOW_("%i"), lwr);
PrintAndLogEx(INFO, "password check..... %s", (bpwc) ? _RED_("on") : _GREEN_("off"));
PrintAndLogEx(INFO, "read after write... %s", (braw) ? "on" : "off");
PrintAndLogEx(NORMAL, "");
2021-06-25 01:27:12 +08:00
PrintAndLogEx(INFO, "--------- " _CYAN_("Protection") " ------------");
PrintAndLogEx(INFO, "first word read protected.... %i", fwrp);
PrintAndLogEx(INFO, "last word read protected..... %i", lwrp);
PrintAndLogEx(INFO, "first word write inhibited... %i", fwwi);
PrintAndLogEx(INFO, "last word write inhibited.... %i", lwwi);
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, "zero values may indicate read protection");
PrintAndLogEx(NORMAL, "");
}
2020-06-15 20:31:34 +08:00
2020-11-20 06:18:04 +08:00
static int em4x50_load_file(const char *filename, uint8_t *data, size_t data_len, size_t *bytes_read) {
2020-06-15 20:31:34 +08:00
uint8_t *dump = NULL;
int res = PM3_SUCCESS;
DumpFileType_t dftype = getfiletype(filename);
switch (dftype) {
case BIN: {
res = loadFile_safe(filename, ".bin", (void **)&dump, bytes_read);
break;
}
case EML: {
res = loadFileEML_safe(filename, (void **)&dump, bytes_read);
break;
}
case JSON: {
dump = calloc(DUMP_FILESIZE, sizeof(uint8_t));
if (dump == NULL) {
PrintAndLogEx(ERR, "error, cannot allocate memory ");
return PM3_EMALLOC;
}
res = loadFileJSON(filename, (void *)dump, DUMP_FILESIZE, bytes_read, NULL);
break;
}
case DICTIONARY: {
free(dump);
PrintAndLogEx(ERR, "Error: Only BIN/JSON/EML formats allowed");
return PM3_EINVARG;
}
}
if ((res != PM3_SUCCESS) && (*bytes_read != DUMP_FILESIZE)) {
free(dump);
2020-11-20 06:18:04 +08:00
return PM3_EFILE;
}
2021-05-07 14:10:19 +08:00
// sanity check, valid em4x50 data?
uint32_t serial = bytes_to_num(dump + 4 * EM4X50_DEVICE_SERIAL, 4);
uint32_t device_id = bytes_to_num(dump + 4 * EM4X50_DEVICE_ID, 4);
2020-11-20 06:18:04 +08:00
if (serial == device_id) {
PrintAndLogEx(WARNING, "No valid EM4x50 data in file %s", filename);
2021-04-19 02:19:49 +08:00
free(dump);
2020-11-20 06:18:04 +08:00
return PM3_ENODATA;
2020-06-15 20:31:34 +08:00
}
memcpy(data, dump, *bytes_read);
free(dump);
2020-11-20 06:18:04 +08:00
return PM3_SUCCESS;
}
2020-06-15 20:31:34 +08:00
static void em4x50_seteml(uint8_t *src, uint32_t offset, uint32_t numofbytes) {
// fast push mode
2021-08-22 02:21:08 +08:00
g_conn.block_after_ACK = true;
for (size_t i = offset; i < numofbytes; i += PM3_CMD_DATA_SIZE) {
size_t len = MIN((numofbytes - i), PM3_CMD_DATA_SIZE);
if (len == numofbytes - i) {
// Disable fast mode on last packet
2021-08-22 02:21:08 +08:00
g_conn.block_after_ACK = false;
}
clearCommandBuffer();
SendCommandOLD(CMD_LF_EM4X50_ESET, i, len, 0, src + i, len);
}
}
2020-11-20 06:18:04 +08:00
int CmdEM4x50ELoad(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "lf em 4x50 eload",
2021-04-19 02:03:23 +08:00
"Loads EM4x50 tag dump (bin/eml/json) into emulator memory on device",
2020-12-09 19:18:01 +08:00
"lf em 4x50 eload -f mydump.bin\n"
);
void *argtable[] = {
arg_param_begin,
2022-01-01 16:39:05 +08:00
arg_str1("f", "file", "<fn>", "dump filename (bin/eml/json)"),
arg_param_end
};
2020-11-20 06:18:04 +08:00
CLIExecWithReturn(ctx, Cmd, argtable, true);
2020-12-09 19:18:01 +08:00
int fnlen = 0;
char filename[FILE_PATH_SIZE] = {0};
CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen);
2020-11-20 06:18:04 +08:00
CLIParserFree(ctx);
// read data from dump file; file type has to be "bin", "eml" or "json"
2020-12-09 19:18:01 +08:00
size_t bytes_read = 0;
uint8_t data[DUMP_FILESIZE] = {0x0};
2020-11-20 06:18:04 +08:00
if (em4x50_load_file(filename, data, DUMP_FILESIZE, &bytes_read) != PM3_SUCCESS) {
PrintAndLogEx(FAILED, "Read error");
return PM3_EFILE;
}
2020-11-20 06:18:04 +08:00
// upload to emulator memory
2021-04-19 02:03:23 +08:00
PrintAndLogEx(INFO, "Uploading to emulator memory contents of " _YELLOW_("%s"), filename);
em4x50_seteml(data, 0, DUMP_FILESIZE);
2020-12-12 21:46:40 +08:00
2021-04-19 02:03:23 +08:00
PrintAndLogEx(INFO, "Done!");
2020-11-20 06:18:04 +08:00
return PM3_SUCCESS;
}
int CmdEM4x50ESave(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "lf em 4x50 esave",
"Saves bin/eml/json dump file of emulator memory.",
2020-12-09 19:18:01 +08:00
"lf em 4x50 esave -> use UID as filename\n"
2021-04-19 02:03:23 +08:00
"lf em 4x50 esave -f mydump\n"
2020-11-20 06:18:04 +08:00
);
void *argtable[] = {
arg_param_begin,
2022-01-01 16:39:05 +08:00
arg_str0("f", "file", "<fn>", "specifiy filename"),
2020-11-20 06:18:04 +08:00
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);
2020-12-09 19:18:01 +08:00
int fnlen = 0;
char filename[FILE_PATH_SIZE] = {0};
CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen);
CLIParserFree(ctx);
// download emulator memory
PrintAndLogEx(SUCCESS, "Reading emulator memory...");
2020-12-09 19:18:01 +08:00
uint8_t data[DUMP_FILESIZE] = {0x0};
if (GetFromDevice(BIG_BUF_EML, data, DUMP_FILESIZE, 0, NULL, 0, NULL, 2500, false) == false) {
2020-11-20 06:18:04 +08:00
PrintAndLogEx(WARNING, "Fail, transfer from device time-out");
return PM3_ETIMEOUT;
}
2020-12-12 21:46:40 +08:00
2020-11-20 06:18:04 +08:00
// valid em4x50 data?
2020-12-09 19:18:01 +08:00
uint32_t serial = bytes_to_num(data + 4 * EM4X50_DEVICE_SERIAL, 4);
uint32_t device_id = bytes_to_num(data + 4 * EM4X50_DEVICE_ID, 4);
2020-11-20 06:18:04 +08:00
if (serial == device_id) {
PrintAndLogEx(WARNING, "No valid em4x50 data in flash memory.");
return PM3_ENODATA;
2020-06-30 23:59:06 +08:00
}
2020-12-12 21:46:40 +08:00
2020-11-20 06:18:04 +08:00
// user supplied filename?
2020-12-09 19:18:01 +08:00
if (fnlen == 0) {
2020-11-20 06:18:04 +08:00
PrintAndLogEx(INFO, "Using UID as filename");
2020-12-09 19:18:01 +08:00
char *fptr = filename;
2020-11-20 06:18:04 +08:00
fptr += snprintf(fptr, sizeof(filename), "lf-4x50-");
FillFileNameByUID(fptr, (uint8_t *)&data[4 * EM4X50_DEVICE_ID], "-dump", 4);
2020-06-30 23:59:06 +08:00
}
2020-11-20 06:18:04 +08:00
saveFile(filename, ".bin", data, DUMP_FILESIZE);
saveFileEML(filename, data, DUMP_FILESIZE, 4);
saveFileJSON(filename, jsfEM4x50, data, DUMP_FILESIZE, NULL);
2020-06-30 23:59:06 +08:00
return PM3_SUCCESS;
}
2020-12-12 02:55:15 +08:00
int CmdEM4x50EView(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "lf em 4x50 eview",
"Displays em4x50 content of emulator memory.",
"lf em 4x50 eview\n"
);
void *argtable[] = {
arg_param_begin,
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);
CLIParserFree(ctx);
// download emulator memory
PrintAndLogEx(SUCCESS, "Reading emulator memory...");
uint8_t data[DUMP_FILESIZE] = {0x0};
if (GetFromDevice(BIG_BUF_EML, data, DUMP_FILESIZE, 0, NULL, 0, NULL, 2500, false) == false) {
PrintAndLogEx(WARNING, "Fail, transfer from device time-out");
return PM3_ETIMEOUT;
}
2020-12-24 02:51:10 +08:00
2020-12-12 02:55:15 +08:00
// valid em4x50 data?
uint32_t serial = bytes_to_num(data + 4 * EM4X50_DEVICE_SERIAL, 4);
uint32_t device_id = bytes_to_num(data + 4 * EM4X50_DEVICE_ID, 4);
if (serial == device_id) {
PrintAndLogEx(WARNING, "No valid em4x50 data in emulator memory.");
return PM3_ENODATA;
}
em4x50_word_t words[EM4X50_NO_WORDS];
for (int i = 0; i < EM4X50_NO_WORDS; i++) {
memcpy(words[i].byte, data + i * 4, 4);
}
print_result(words, 0, EM4X50_NO_WORDS - 1);
PrintAndLogEx(NORMAL, "");
return PM3_SUCCESS;
}
2020-11-20 06:18:04 +08:00
int CmdEM4x50Login(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "lf em 4x50 login",
2020-11-20 06:18:04 +08:00
"Login into EM4x50 tag.",
2020-12-09 19:18:01 +08:00
"lf em 4x50 login -p 12345678 -> login with password 12345678\n"
);
void *argtable[] = {
arg_param_begin,
arg_str1("p", "passsword", "<hex>", "password, 4 bytes, lsb"),
arg_param_end
};
2020-11-20 06:18:04 +08:00
2020-12-12 21:46:40 +08:00
CLIExecWithReturn(ctx, Cmd, argtable, true);
2020-12-09 19:18:01 +08:00
int pwd_len = 0;
uint8_t pwd[4] = {0x0};
CLIGetHexWithReturn(ctx, 1, pwd, &pwd_len);
CLIParserFree(ctx);
if (pwd_len != 4) {
PrintAndLogEx(FAILED, "password length must be 4 bytes");
return PM3_EINVARG;
2020-12-12 21:46:40 +08:00
}
2020-11-20 06:18:04 +08:00
uint32_t password = BYTES2UINT32_BE(pwd);
2020-11-20 06:18:04 +08:00
// start
clearCommandBuffer();
2020-12-09 19:18:01 +08:00
PacketResponseNG resp;
2020-11-20 06:18:04 +08:00
SendCommandNG(CMD_LF_EM4X50_LOGIN, (uint8_t *)&password, sizeof(password));
WaitForResponse(CMD_LF_EM4X50_LOGIN, &resp);
// print response
if (resp.status == PM3_SUCCESS)
PrintAndLogEx(SUCCESS, "Login " _GREEN_("ok"));
else
PrintAndLogEx(FAILED, "Login " _RED_("failed"));
return resp.status;
}
int CmdEM4x50Brute(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "lf em 4x50 brute",
2021-06-25 01:27:12 +08:00
"Tries to bruteforce the password of a EM4x50 card.\n"
2020-12-09 19:18:01 +08:00
"Function can be stopped by pressing pm3 button.",
2021-06-25 01:27:12 +08:00
"lf em 4x50 brute --first 12330000 --last 12340000 -> tries pwds from 0x12330000 to 0x1234000000\n"
2020-11-20 06:18:04 +08:00
);
void *argtable[] = {
arg_param_begin,
2020-12-09 19:18:01 +08:00
arg_str1(NULL, "first", "<hex>", "first password (start), 4 bytes, lsb"),
arg_str1(NULL, "last", "<hex>", "last password (stop), 4 bytes, lsb"),
2020-11-20 06:18:04 +08:00
arg_param_end
};
2020-12-12 21:46:40 +08:00
CLIExecWithReturn(ctx, Cmd, argtable, true);
2020-12-09 19:18:01 +08:00
int first_len = 0;
2020-12-12 21:46:40 +08:00
uint8_t first[4] = {0, 0, 0, 0};
2020-12-09 19:18:01 +08:00
CLIGetHexWithReturn(ctx, 1, first, &first_len);
int last_len = 0;
2020-12-12 21:46:40 +08:00
uint8_t last[4] = {0, 0, 0, 0};
2020-12-09 19:18:01 +08:00
CLIGetHexWithReturn(ctx, 2, last, &last_len);
CLIParserFree(ctx);
2020-11-20 06:18:04 +08:00
2020-12-09 19:18:01 +08:00
if (first_len != 4) {
PrintAndLogEx(FAILED, "password length must be 4 bytes");
return PM3_EINVARG;
2020-12-12 21:46:40 +08:00
}
if (last_len != 4) {
PrintAndLogEx(FAILED, "password length must be 4 bytes");
return PM3_EINVARG;
}
2020-12-09 19:18:01 +08:00
em4x50_data_t etd;
etd.password1 = BYTES2UINT32_BE(first);
etd.password2 = BYTES2UINT32_BE(last);
2020-12-09 19:18:01 +08:00
// 27 passwords/second (empirical value)
2020-12-12 21:46:40 +08:00
const int speed = 27;
2020-11-20 06:18:04 +08:00
// print some information
2020-12-09 19:18:01 +08:00
int no_iter = etd.password2 - etd.password1 + 1;
int dur_s = no_iter / speed;
int dur_h = dur_s / 3600;
int dur_m = (dur_s - dur_h * 3600) / 60;
2020-11-20 06:18:04 +08:00
dur_s -= dur_h * 3600 + dur_m * 60;
2021-06-25 16:02:30 +08:00
PrintAndLogEx(INFO, "Trying " _YELLOW_("%i") " passwords in range [0x%08x, 0x%08x]"
2020-12-12 21:46:40 +08:00
, no_iter
, etd.password1
, etd.password2
);
2021-06-25 16:02:30 +08:00
PrintAndLogEx(INFO, "Estimated duration: %ih %im %is", dur_h, dur_m, dur_s);
2020-11-20 06:18:04 +08:00
// start
clearCommandBuffer();
2020-12-09 19:18:01 +08:00
PacketResponseNG resp;
2020-11-20 06:18:04 +08:00
SendCommandNG(CMD_LF_EM4X50_BRUTE, (uint8_t *)&etd, sizeof(etd));
WaitForResponse(CMD_LF_EM4X50_BRUTE, &resp);
2020-11-20 06:18:04 +08:00
// print response
2020-12-09 19:18:01 +08:00
if (resp.status == PM3_SUCCESS)
2021-06-25 16:02:30 +08:00
PrintAndLogEx(SUCCESS, "found valid password [ " _GREEN_("%08"PRIX32) " ]", resp.data.asDwords[0]);
2020-11-20 06:18:04 +08:00
else
2021-06-25 16:02:30 +08:00
PrintAndLogEx(WARNING, "brute pwd failed");
2020-11-20 06:18:04 +08:00
return PM3_SUCCESS;
}
2020-12-09 19:18:01 +08:00
// upload passwords from given dictionary to device and start check;
// if no filename is given dictionary "t55xx_default_pwds.dic" is used
2020-11-20 06:18:04 +08:00
int CmdEM4x50Chk(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "lf em 4x50 chk",
2021-06-25 01:27:12 +08:00
"Run dictionary key recovery against EM4x50 card.",
2020-12-09 19:18:01 +08:00
"lf em 4x50 chk -> uses T55xx default dictionary\n"
"lf em 4x50 chk -f my.dic"
2020-11-20 06:18:04 +08:00
);
void *argtable[] = {
arg_param_begin,
2022-01-01 16:39:05 +08:00
arg_str0("f", "file", "<fn>", "specify dictionary filename"),
2020-11-20 06:18:04 +08:00
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);
2020-12-09 19:18:01 +08:00
int fnlen = 0;
char filename[FILE_PATH_SIZE] = {0};
CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen);
2020-11-20 06:18:04 +08:00
CLIParserFree(ctx);
2020-12-12 21:46:40 +08:00
2020-12-09 19:18:01 +08:00
if (IfPm3Flash() == false) {
PrintAndLogEx(WARNING, "no flash memory available");
return PM3_EFLASH;
}
2020-11-20 06:18:04 +08:00
// no filename -> default = t55xx_default_pwds
if (strlen(filename) == 0) {
snprintf(filename, sizeof(filename), "t55xx_default_pwds");
PrintAndLogEx(INFO, "treating file as T55xx keys");
}
2020-12-09 19:18:01 +08:00
// load keys
uint8_t *keys = NULL;
2020-12-09 19:18:01 +08:00
uint32_t key_count = 0;
int res = loadFileDICTIONARY_safe(filename, (void **)&keys, 4, &key_count);
if (res != PM3_SUCCESS || key_count == 0) {
free(keys);
return res;
}
2020-12-09 19:18:01 +08:00
2021-06-26 04:13:41 +08:00
uint8_t *pkeys = keys;
uint64_t t1 = msclock();
2020-12-12 21:46:40 +08:00
2020-11-20 06:18:04 +08:00
PrintAndLogEx(INFO, "You can cancel this operation by pressing the pm3 button");
2021-06-26 05:35:36 +08:00
// block with 2000 bytes -> 500 keys
2020-12-09 19:18:01 +08:00
uint8_t destfn[32] = "em4x50_chk.bin";
2020-12-12 21:46:40 +08:00
PacketResponseNG resp;
int bytes_remaining = key_count * 4;
int status = PM3_EFAILED;
while (bytes_remaining > 0) {
2020-12-12 21:46:40 +08:00
PrintAndLogEx(INPLACE, "Remaining keys: %i ", bytes_remaining / 4);
// upload to flash.
size_t n = MIN(bytes_remaining, 2000);
res = flashmem_spiffs_load((char *)destfn, keys, n);
2020-11-20 06:18:04 +08:00
if (res != PM3_SUCCESS) {
2020-12-05 05:42:54 +08:00
PrintAndLogEx(WARNING, "SPIFFS upload failed");
2020-11-20 06:18:04 +08:00
return res;
}
clearCommandBuffer();
SendCommandNG(CMD_LF_EM4X50_CHK, destfn, sizeof(destfn));
WaitForResponseTimeoutW(CMD_LF_EM4X50_CHK, &resp, -1, false);
2020-12-12 21:46:40 +08:00
status = resp.status;
if ((status == PM3_SUCCESS) || (status == PM3_EOPABORTED))
break;
bytes_remaining -= n;
keys += n;
2020-11-20 06:18:04 +08:00
}
2020-12-09 19:18:01 +08:00
2021-06-26 04:13:41 +08:00
free(pkeys);
2020-12-09 19:18:01 +08:00
PrintAndLogEx(NORMAL, "");
2020-12-12 21:46:40 +08:00
2020-11-20 06:18:04 +08:00
if (status == PM3_SUCCESS) {
2021-06-25 16:02:30 +08:00
uint32_t pwd = BYTES2UINT32(resp.data.asBytes);
PrintAndLogEx(SUCCESS, "found valid password [ " _GREEN_("%08"PRIX32) " ]", pwd);
2020-11-20 06:18:04 +08:00
} else {
PrintAndLogEx(FAILED, "No key found");
}
2021-06-25 16:02:30 +08:00
t1 = msclock() - t1;
PrintAndLogEx(SUCCESS, "\ntime in check pwd " _YELLOW_("%.0f") " seconds\n", (float)t1 / 1000.0);
2020-11-20 06:18:04 +08:00
return PM3_SUCCESS;
}
//quick test for EM4x50 tag
bool detect_4x50_block(void) {
em4x50_data_t etd = {
.pwd_given = false,
.addr_given = true,
.addresses = (EM4X50_DEVICE_SERIAL << 8) | EM4X50_DEVICE_SERIAL,
2020-11-20 06:18:04 +08:00
};
em4x50_word_t words[EM4X50_NO_WORDS];
return (em4x50_read(&etd, words) == PM3_SUCCESS);
}
2020-11-20 06:18:04 +08:00
int read_em4x50_uid(void) {
em4x50_data_t etd = {
.pwd_given = false,
.addr_given = true,
.addresses = (EM4X50_DEVICE_SERIAL << 8) | EM4X50_DEVICE_SERIAL,
};
em4x50_word_t words[EM4X50_NO_WORDS];
int res = em4x50_read(&etd, words);
if (res == PM3_SUCCESS)
PrintAndLogEx(INFO, " Serial: " _GREEN_("%s"), sprint_hex(words[EM4X50_DEVICE_SERIAL].byte, 4));
return res;
}
2020-06-26 20:20:46 +08:00
2020-12-09 19:18:01 +08:00
// envoke reading
// - with given address (option b) (and optional password if address is
// read protected) -> selective read mode
int em4x50_read(em4x50_data_t *etd, em4x50_word_t *out) {
2020-07-01 00:50:38 +08:00
2020-06-30 23:59:06 +08:00
em4x50_data_t edata = { .pwd_given = false, .addr_given = false };
2020-07-01 00:50:38 +08:00
if (etd != NULL) {
edata = *etd;
}
clearCommandBuffer();
SendCommandNG(CMD_LF_EM4X50_READ, (uint8_t *)&edata, sizeof(edata));
2020-06-26 20:20:46 +08:00
PacketResponseNG resp;
2021-02-07 02:59:34 +08:00
if (!WaitForResponseTimeout(CMD_LF_EM4X50_READ, &resp, TIMEOUT_CMD)) {
2020-09-06 05:57:06 +08:00
PrintAndLogEx(WARNING, "(em4x50) timeout while waiting for reply.");
return PM3_ETIMEOUT;
}
2020-07-01 00:50:38 +08:00
2020-11-20 06:18:04 +08:00
if (resp.status != PM3_SUCCESS)
return PM3_ESOFT;
2020-07-01 00:50:38 +08:00
uint8_t *data = resp.data.asBytes;
em4x50_word_t words[EM4X50_NO_WORDS];
prepare_result(data, etd->addresses & 0xFF, (etd->addresses >> 8) & 0xFF, words);
2020-06-26 20:20:46 +08:00
2020-11-20 06:18:04 +08:00
if (out != NULL)
memcpy(out, &words, sizeof(em4x50_word_t) * EM4X50_NO_WORDS);
2020-07-01 00:50:38 +08:00
print_result(words, etd->addresses & 0xFF, (etd->addresses >> 8) & 0xFF);
2020-09-05 19:25:57 +08:00
return PM3_SUCCESS;
2020-06-26 20:20:46 +08:00
}
int CmdEM4x50Read(const char *Cmd) {
CLIParserContext *ctx;
2021-03-10 05:02:08 +08:00
CLIParserInit(&ctx, "lf em 4x50 rdbl",
2020-11-13 07:50:49 +08:00
"Reads single EM4x50 block/word.",
2020-12-09 19:18:01 +08:00
"lf em 4x50 rdbl -b 3\n"
"lf em 4x50 rdbl -b 32 -p 12345678 -> reads block 32 with pwd 0x12345678\n"
);
void *argtable[] = {
arg_param_begin,
arg_int1("b", "block", "<dec>", "block/word address"),
2020-12-09 19:18:01 +08:00
arg_str0("p", "pwd", "<hex>", "password, 4 hex bytes, lsb"),
arg_param_end
};
2020-06-26 20:20:46 +08:00
CLIExecWithReturn(ctx, Cmd, argtable, true);
2020-12-12 21:46:40 +08:00
2020-12-09 19:18:01 +08:00
int addr = arg_get_int_def(ctx, 1, 0);
int pwd_len = 0;
uint8_t pwd[4] = {0x0};
CLIGetHexWithReturn(ctx, 2, pwd, &pwd_len);
CLIParserFree(ctx);
if (addr <= 0 || addr >= EM4X50_NO_WORDS) {
return PM3_EINVARG;
2020-12-12 21:46:40 +08:00
}
2020-10-22 06:42:18 +08:00
2020-12-09 19:18:01 +08:00
em4x50_data_t etd;
// init
memset(&etd, 0x00, sizeof(em4x50_data_t));
etd.pwd_given = false;
etd.addresses = (addr << 8) | addr;
etd.addr_given = true;
if (pwd_len) {
if (pwd_len != 4) {
PrintAndLogEx(FAILED, "password length must be 4 bytes instead of %d", pwd_len);
return PM3_EINVARG;
2020-11-20 06:18:04 +08:00
} else {
etd.password1 = BYTES2UINT32_BE(pwd);
2020-11-20 06:18:04 +08:00
etd.pwd_given = true;
}
}
2020-11-20 06:18:04 +08:00
return em4x50_read(&etd, NULL);
2020-09-05 19:25:57 +08:00
}
2020-09-28 05:22:51 +08:00
2020-12-09 19:18:01 +08:00
// envoke reading of a EM4x50 tag which has to be on the antenna because
// decoding is done by the device (not on client side)
2020-11-20 06:18:04 +08:00
int CmdEM4x50Info(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "lf em 4x50 info",
2020-11-20 06:18:04 +08:00
"Tag information EM4x50.",
"lf em 4x50 info\n"
2021-06-25 01:27:12 +08:00
"lf em 4x50 info -v -> show data section\n"
"lf em 4x50 info -p 12345678 -> uses pwd 0x12345678\n"
);
2020-09-28 05:39:04 +08:00
void *argtable[] = {
arg_param_begin,
2020-12-09 19:18:01 +08:00
arg_str0("p", "pwd", "<hex>", "password, 4 hex bytes, lsb"),
arg_lit0("v", "verbose", "additional output of data section"),
arg_param_end
};
2020-10-22 06:42:18 +08:00
CLIExecWithReturn(ctx, Cmd, argtable, true);
2020-12-09 19:18:01 +08:00
int pwd_len = 0;
2020-12-12 21:46:40 +08:00
uint8_t pwd[4] = {0x0};
2020-12-09 19:18:01 +08:00
CLIGetHexWithReturn(ctx, 1, pwd, &pwd_len);
bool verb = arg_get_lit(ctx, 2);
2020-12-09 19:18:01 +08:00
CLIParserFree(ctx);
2020-12-12 21:46:40 +08:00
em4x50_data_t etd = {.pwd_given = false};
2020-12-09 19:18:01 +08:00
if (pwd_len) {
if (pwd_len != 4) {
PrintAndLogEx(FAILED, "password length must be 4 bytes instead of %d", pwd_len);
2020-11-20 06:18:04 +08:00
return PM3_EINVARG;
} else {
etd.password1 = BYTES2UINT32_BE(pwd);
2020-11-20 06:18:04 +08:00
etd.pwd_given = true;
}
}
2020-09-28 05:39:04 +08:00
clearCommandBuffer();
2020-11-20 06:18:04 +08:00
SendCommandNG(CMD_LF_EM4X50_INFO, (uint8_t *)&etd, sizeof(etd));
PacketResponseNG resp;
2021-02-07 02:59:34 +08:00
if (!WaitForResponseTimeout(CMD_LF_EM4X50_INFO, &resp, TIMEOUT_CMD)) {
2020-11-20 06:18:04 +08:00
PrintAndLogEx(WARNING, "Timeout while waiting for reply.");
return PM3_ETIMEOUT;
}
2020-12-12 21:46:40 +08:00
if (resp.status == PM3_SUCCESS)
print_info_result(resp.data.asBytes, verb);
2020-09-28 05:39:04 +08:00
else
2020-11-20 06:18:04 +08:00
PrintAndLogEx(FAILED, "Reading tag " _RED_("failed"));
2020-09-28 05:39:04 +08:00
2020-12-09 19:18:01 +08:00
return resp.status;
2020-09-28 05:39:04 +08:00
}
2020-10-06 04:59:08 +08:00
int CmdEM4x50Reader(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "lf em 4x50 reader",
"Shows standard read data of EM4x50 tag.",
"lf em 4x50 reader\n"
"lf em 4x50 reader -@ -> continuous reader mode"
);
void *argtable[] = {
arg_param_begin,
arg_lit0("@", NULL, "optional - continuous reader mode"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);
bool cm = arg_get_lit(ctx, 1);
CLIParserFree(ctx);
// start
do {
2020-12-09 19:18:01 +08:00
PacketResponseNG resp;
clearCommandBuffer();
SendCommandNG(CMD_LF_EM4X50_READER, 0, 0);
2020-12-01 03:31:02 +08:00
WaitForResponseTimeoutW(CMD_LF_EM4X50_READER, &resp, -1, false);
2020-12-09 19:18:01 +08:00
// iceman, misuse of return status code.
int now = resp.status;
if (now > 0) {
em4x50_word_t words[EM4X50_NO_WORDS];
prepare_result(resp.data.asBytes, 0, now - 1, words);
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, " word (msb) | word (lsb) ");
PrintAndLogEx(INFO, "-------------+-------------");
for (int i = 0; i < now; i++) {
2020-12-09 19:18:01 +08:00
char r[30];
memset(r, 0, sizeof(r));
for (int j = 3; j >= 0; j--) {
sprintf(r + strlen(r), "%02x ", reflect8(words[i].byte[j]));
2020-12-09 19:18:01 +08:00
}
2020-12-09 19:18:01 +08:00
PrintAndLogEx(INFO, _GREEN_(" %s") "| %s", sprint_hex(words[i].byte, 4), r);
}
2020-12-12 21:46:40 +08:00
PrintAndLogEx(INFO, "-------------+-------------");
}
} while (cm && !kbd_enter_pressed());
return PM3_SUCCESS;
}
2020-11-20 06:18:04 +08:00
int CmdEM4x50Dump(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "lf em 4x50 dump",
2021-04-19 02:03:23 +08:00
"Reads all blocks/words from EM4x50 tag and saves dump in bin/eml/json format",
2020-12-09 19:18:01 +08:00
"lf em 4x50 dump\n"
2021-04-19 02:03:23 +08:00
"lf em 4x50 dump -f mydump\n"
"lf em 4x50 dump -p 12345678\n"
2021-04-19 02:03:23 +08:00
"lf em 4x50 dump -f mydump -p 12345678"
2020-11-20 06:18:04 +08:00
);
void *argtable[] = {
arg_param_begin,
2022-01-01 16:39:05 +08:00
arg_str0("f", "file", "<fn>", "specify dump filename (bin/eml/json)"),
2020-12-09 19:18:01 +08:00
arg_str0("p", "pwd", "<hex>", "password, 4 hex bytes, lsb"),
2020-11-20 06:18:04 +08:00
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);
2020-12-09 19:18:01 +08:00
int fnLen = 0;
char filename[FILE_PATH_SIZE] = {0};
CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnLen);
int pwd_len = 0;
uint8_t pwd[4] = {0x0};
CLIGetHexWithReturn(ctx, 2, pwd, &pwd_len);
CLIParserFree(ctx);
em4x50_data_t etd = {.pwd_given = false};
if (pwd_len) {
if (pwd_len != 4) {
PrintAndLogEx(FAILED, "password length must be 4 bytes");
CLIParserFree(ctx);
2020-11-20 06:18:04 +08:00
return PM3_EINVARG;
} else {
etd.password1 = BYTES2UINT32_BE(pwd);
2020-11-20 06:18:04 +08:00
etd.pwd_given = true;
}
}
PrintAndLogEx(INFO, "Reading EM4x50 tag");
clearCommandBuffer();
SendCommandNG(CMD_LF_EM4X50_INFO, (uint8_t *)&etd, sizeof(etd));
PacketResponseNG resp;
2021-02-07 02:59:34 +08:00
if (!WaitForResponseTimeout(CMD_LF_EM4X50_INFO, &resp, TIMEOUT_CMD)) {
2020-11-20 06:18:04 +08:00
PrintAndLogEx(WARNING, "Timeout while waiting for reply.");
return PM3_ETIMEOUT;
}
2020-12-09 19:18:01 +08:00
if (resp.status != PM3_SUCCESS) {
2020-11-20 06:18:04 +08:00
PrintAndLogEx(FAILED, "Reading tag " _RED_("failed"));
return PM3_ESOFT;
}
// structured format
em4x50_word_t words[EM4X50_NO_WORDS];
prepare_result(resp.data.asBytes, 0, EM4X50_NO_WORDS - 1, words);
// result output
PrintAndLogEx(INFO, _YELLOW_("EM4x50 data:"));
print_result(words, 0, EM4X50_NO_WORDS - 1);
// user supplied filename?
if (fnLen == 0) {
PrintAndLogEx(INFO, "Using UID as filename");
2020-12-09 19:18:01 +08:00
char *fptr = filename;
2020-11-20 06:18:04 +08:00
fptr += sprintf(fptr, "lf-4x50-");
FillFileNameByUID(fptr, words[EM4X50_DEVICE_ID].byte, "-dump", 4);
}
2020-12-09 19:18:01 +08:00
uint8_t data[DUMP_FILESIZE] = {0};
for (int i = 0; i < EM4X50_NO_WORDS; i++) {
2020-11-20 06:18:04 +08:00
memcpy(data + (i * 4), words[i].byte, 4);
2020-12-09 19:18:01 +08:00
}
2020-11-20 06:18:04 +08:00
saveFile(filename, ".bin", data, sizeof(data));
2020-12-09 19:18:01 +08:00
saveFileEML(filename, data, sizeof(data), 4);
2020-11-20 06:18:04 +08:00
saveFileJSON(filename, jsfEM4x50, data, sizeof(data), NULL);
return PM3_SUCCESS;
}
2020-12-09 19:18:01 +08:00
// envoke writing a single word (32 bit) to a EM4x50 tag
2020-11-20 06:18:04 +08:00
int CmdEM4x50Write(const char *Cmd) {
CLIParserContext *ctx;
2020-12-09 19:18:01 +08:00
CLIParserInit(&ctx, "lf em 4x50 wrbl",
2020-11-20 06:18:04 +08:00
"Writes single block/word to EM4x50 tag.",
2020-12-09 19:18:01 +08:00
"lf em 4x50 wrbl -b 3 -d 4f22e7ff \n"
"lf em 4x50 wrbl -b 3 -d 4f22e7ff -p 12345678\n"
);
void *argtable[] = {
arg_param_begin,
arg_int1("b", "block", "<dec>", "block/word address, dec"),
arg_str1("d", "data", "<hex>", "data, 4 bytes, lsb"),
2020-12-09 19:18:01 +08:00
arg_str0("p", "pwd", "<hex>", "password, 4 bytes, lsb"),
arg_param_end
};
2020-12-12 21:46:40 +08:00
CLIExecWithReturn(ctx, Cmd, argtable, true);
2020-12-09 19:18:01 +08:00
int addr = arg_get_int_def(ctx, 1, 0);
2020-12-12 21:46:40 +08:00
2020-12-09 19:18:01 +08:00
int word_len = 0;
uint8_t word[4] = {0x0};
CLIGetHexWithReturn(ctx, 2, word, &word_len);
int pwd_len = 0;
uint8_t pwd[4] = {0x0};
CLIGetHexWithReturn(ctx, 3, pwd, &pwd_len);
CLIParserFree(ctx);
2020-12-12 21:46:40 +08:00
2020-11-20 06:18:04 +08:00
if (addr <= 0 || addr >= EM4X50_NO_WORDS) {
PrintAndLogEx(FAILED, "address has to be within range [0, 31]");
return PM3_EINVARG;
2020-12-12 21:46:40 +08:00
}
2020-12-09 19:18:01 +08:00
if (word_len != 4) {
PrintAndLogEx(FAILED, "word/data length must be 4 bytes instead of %d", word_len);
2020-11-20 06:18:04 +08:00
return PM3_EINVARG;
}
2020-12-09 19:18:01 +08:00
2020-12-12 21:46:40 +08:00
em4x50_data_t etd = {.pwd_given = false};
2020-12-09 19:18:01 +08:00
if (pwd_len) {
if (pwd_len != 4) {
PrintAndLogEx(FAILED, "password length must be 4 bytes instead of %d", pwd_len);
return PM3_EINVARG;
} else {
etd.password1 = BYTES2UINT32_BE(pwd);
etd.pwd_given = true;
}
}
2020-12-09 19:18:01 +08:00
etd.addresses = (addr << 8) | addr;
etd.addr_given = true;
etd.word = BYTES2UINT32_BE(word);
clearCommandBuffer();
2020-11-20 06:18:04 +08:00
SendCommandNG(CMD_LF_EM4X50_WRITE, (uint8_t *)&etd, sizeof(etd));
PacketResponseNG resp;
2021-02-07 02:59:34 +08:00
if (!WaitForResponseTimeout(CMD_LF_EM4X50_WRITE, &resp, TIMEOUT_CMD)) {
2020-11-20 06:18:04 +08:00
PrintAndLogEx(WARNING, "Timeout while waiting for reply.");
return PM3_ETIMEOUT;
}
2020-12-09 19:18:01 +08:00
int status = resp.status;
2020-11-20 06:18:04 +08:00
if (status == PM3_ETEAROFF)
2020-12-09 19:18:01 +08:00
return status;
2020-11-20 06:18:04 +08:00
if (status != PM3_SUCCESS) {
PrintAndLogEx(FAILED, "Writing " _RED_("failed"));
return PM3_ESOFT;
}
2020-11-20 06:18:04 +08:00
// display result of writing operation in structured format
uint8_t *data = resp.data.asBytes;
em4x50_word_t words[EM4X50_NO_WORDS];
prepare_result(data, addr, addr, words);
print_result(words, addr, addr);
PrintAndLogEx(SUCCESS, "Successfully wrote to tag");
2020-12-09 19:18:01 +08:00
PrintAndLogEx(HINT, "Try `" _YELLOW_("lf em 4x50 rdbl -a %u") "` - to read your data", addr);
return PM3_SUCCESS;
}
2020-12-09 19:18:01 +08:00
// envokes changing the password of EM4x50 tag
2020-11-20 06:18:04 +08:00
int CmdEM4x50WritePwd(const char *Cmd) {
CLIParserContext *ctx;
2021-06-25 01:27:12 +08:00
CLIParserInit(&ctx, "lf em 4x50 wrpwd",
2020-11-20 06:18:04 +08:00
"Writes EM4x50 password.",
2021-06-25 01:27:12 +08:00
"lf em 4x50 wrpwd -p 4f22e7ff -n 12345678"
);
void *argtable[] = {
arg_param_begin,
2020-12-09 19:18:01 +08:00
arg_str1("p", "pwd", "<hex>", "password, 4 hex bytes, lsb"),
arg_str1("n", "new", "<hex>", "new password, 4 hex bytes, lsb"),
arg_param_end
};
2020-12-12 21:46:40 +08:00
CLIExecWithReturn(ctx, Cmd, argtable, true);
2020-12-09 19:18:01 +08:00
int pwd_len = 0;
uint8_t pwd[4] = {0x0};
CLIGetHexWithReturn(ctx, 1, pwd, &pwd_len);
int npwd_len = 0;
uint8_t npwd[4] = {0x0};
CLIGetHexWithReturn(ctx, 2, npwd, &npwd_len);
CLIParserFree(ctx);
2020-12-12 21:46:40 +08:00
em4x50_data_t etd;
2020-12-09 19:18:01 +08:00
if (pwd_len != 4) {
PrintAndLogEx(FAILED, "password length must be 4 bytes instead of %d", pwd_len);
2020-11-20 06:18:04 +08:00
return PM3_EINVARG;
} else {
etd.password1 = BYTES2UINT32_BE(pwd);
2020-11-20 06:18:04 +08:00
}
2020-12-09 19:18:01 +08:00
if (npwd_len != 4) {
PrintAndLogEx(FAILED, "password length must be 4 bytes instead of %d", npwd_len);
2020-11-20 06:18:04 +08:00
return PM3_EINVARG;
} else {
etd.password2 = BYTES2UINT32_BE(npwd);
2020-11-20 06:18:04 +08:00
}
2020-10-27 07:53:50 +08:00
2020-12-09 19:18:01 +08:00
PacketResponseNG resp;
2020-11-20 06:18:04 +08:00
clearCommandBuffer();
SendCommandNG(CMD_LF_EM4X50_WRITEPWD, (uint8_t *)&etd, sizeof(etd));
2021-02-07 02:59:34 +08:00
if (!WaitForResponseTimeout(CMD_LF_EM4X50_WRITEPWD, &resp, TIMEOUT_CMD)) {
2020-11-20 06:18:04 +08:00
PrintAndLogEx(WARNING, "Timeout while waiting for reply.");
return PM3_ETIMEOUT;
2020-10-27 07:53:50 +08:00
}
2020-12-09 19:18:01 +08:00
if (resp.status == PM3_ETEAROFF)
return PM3_SUCCESS;
2020-11-20 06:18:04 +08:00
2020-12-09 19:18:01 +08:00
if (resp.status != PM3_SUCCESS) {
PrintAndLogEx(FAILED, "Writing password (" _RED_("failed") ")");
2020-11-20 06:18:04 +08:00
return PM3_EFAILED;
}
2020-12-09 19:18:01 +08:00
PrintAndLogEx(SUCCESS, "Writing new password %s (%s)"
2020-12-12 21:46:40 +08:00
, sprint_hex_inrow(npwd, sizeof(npwd))
, _GREEN_("ok")
);
return PM3_SUCCESS;
}
2020-10-28 01:18:02 +08:00
2020-12-09 19:18:01 +08:00
// fills EM4x50 tag with zeros including password
2020-11-20 06:18:04 +08:00
int CmdEM4x50Wipe(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "lf em 4x50 wipe",
2020-12-09 19:18:01 +08:00
"Wipes EM4x50 tag by filling it with zeros, including the new password\n"
"Must give a password.",
"lf em 4x50 wipe -p 12345678"
);
2020-10-28 01:18:02 +08:00
void *argtable[] = {
arg_param_begin,
arg_str1("p", "passsword", "<hex>", "password, 4 bytes, lsb"),
arg_param_end
};
2020-10-28 01:18:02 +08:00
2020-12-12 21:46:40 +08:00
CLIExecWithReturn(ctx, Cmd, argtable, true);
2020-12-09 19:18:01 +08:00
int pwd_len = 0;
uint8_t pwd[4] = {0x0};
CLIGetHexWithReturn(ctx, 1, pwd, &pwd_len);
CLIParserFree(ctx);
if (pwd_len != 4) {
PrintAndLogEx(FAILED, "password length must be 4 bytes instead of %d", pwd_len);
CLIParserFree(ctx);
2020-11-20 06:18:04 +08:00
return PM3_EINVARG;
}
2020-10-28 01:18:02 +08:00
2020-12-09 19:18:01 +08:00
em4x50_data_t etd = {.pwd_given = false, .word = 0x0, .password2 = 0x0};
etd.password1 = BYTES2UINT32_BE(pwd);
2020-12-09 19:18:01 +08:00
etd.pwd_given = true;
2020-12-12 21:46:40 +08:00
2020-11-20 06:18:04 +08:00
// clear password
2020-12-09 19:18:01 +08:00
PacketResponseNG resp;
2020-10-28 01:18:02 +08:00
clearCommandBuffer();
2020-11-20 06:18:04 +08:00
SendCommandNG(CMD_LF_EM4X50_WRITEPWD, (uint8_t *)&etd, sizeof(etd));
2021-02-07 02:59:34 +08:00
if (!WaitForResponseTimeout(CMD_LF_EM4X50_WRITEPWD, &resp, TIMEOUT_CMD)) {
2020-10-28 01:18:02 +08:00
PrintAndLogEx(WARNING, "Timeout while waiting for reply.");
return PM3_ETIMEOUT;
}
2020-11-20 06:18:04 +08:00
if (resp.status == PM3_SUCCESS) {
2020-12-09 19:18:01 +08:00
PrintAndLogEx(SUCCESS, "Resetting password to 00000000 (" _GREEN_("ok") ")");
2020-11-20 06:18:04 +08:00
} else {
PrintAndLogEx(FAILED, "Resetting password " _RED_("failed"));
return PM3_ESOFT;
}
// from now on new password 0x0
etd.password1 = 0x0;
2020-12-12 21:46:40 +08:00
2020-11-20 06:18:04 +08:00
// clear data (words 1 to 31)
for (int i = 1; i < EM4X50_DEVICE_SERIAL; i++) {
2020-10-28 01:18:02 +08:00
2020-11-20 06:18:04 +08:00
// no login necessary for blocks 3 to 31
etd.pwd_given = (i <= EM4X50_CONTROL);
2020-12-12 21:46:40 +08:00
2020-11-20 06:18:04 +08:00
PrintAndLogEx(INPLACE, "Wiping block %i", i);
2020-10-28 01:18:02 +08:00
2020-11-20 06:18:04 +08:00
etd.addresses = i << 8 | i;
clearCommandBuffer();
SendCommandNG(CMD_LF_EM4X50_WRITE, (uint8_t *)&etd, sizeof(etd));
2021-02-07 02:59:34 +08:00
if (!WaitForResponseTimeout(CMD_LF_EM4X50_WRITE, &resp, TIMEOUT_CMD)) {
2020-11-20 06:18:04 +08:00
PrintAndLogEx(WARNING, "Timeout while waiting for reply.");
return PM3_ETIMEOUT;
2020-10-28 01:18:02 +08:00
}
2020-12-12 21:46:40 +08:00
if (resp.status != PM3_SUCCESS) {
2020-11-20 06:18:04 +08:00
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(FAILED, "Wiping data " _RED_("failed"));
return PM3_ESOFT;
}
2020-10-28 01:18:02 +08:00
}
2020-11-20 06:18:04 +08:00
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, "Done");
return PM3_SUCCESS;
2020-10-28 01:18:02 +08:00
}
2020-11-20 06:18:04 +08:00
int CmdEM4x50Restore(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "lf em 4x50 restore",
"Restores data from dumpfile (bin/eml/json) onto a EM4x50 tag.\n"
2020-12-09 19:18:01 +08:00
"if used with -u, the filetemplate `lf-4x50-UID-dump.bin` is used as filename",
"lf em 4x50 restore -u 1b5aff5c -> uses lf-4x50-1B5AFF5C-dump.bin\n"
"lf em 4x50 restore -f mydump.eml\n"
"lf em 4x50 restore -u 1b5aff5c -p 12345678\n"
"lf em 4x50 restore -f mydump.eml -p 12345678\n"
);
void *argtable[] = {
arg_param_begin,
2020-12-09 19:18:01 +08:00
arg_str0("u", "uid", "<hex>", "uid, 4 hex bytes, msb"),
2022-01-01 16:39:05 +08:00
arg_str0("f", "file", "<fn>", "specify dump filename (bin/eml/json)"),
2020-12-09 19:18:01 +08:00
arg_str0("p", "pwd", "<hex>", "password, 4 hex bytes, lsb"),
arg_param_end
};
2020-12-12 21:46:40 +08:00
CLIExecWithReturn(ctx, Cmd, argtable, true);
2020-12-09 19:18:01 +08:00
int uidLen = 0;
uint8_t uid[4] = {0x0};
2020-11-20 06:18:04 +08:00
CLIGetHexWithReturn(ctx, 1, uid, &uidLen);
2020-12-09 19:18:01 +08:00
int fnlen = 0;
char filename[FILE_PATH_SIZE] = {0};
CLIParamStrToBuf(arg_get_str(ctx, 2), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen);
int pwd_len = 0;
uint8_t pwd[4] = {0x0};
CLIGetHexWithReturn(ctx, 3, pwd, &pwd_len);
CLIParserFree(ctx);
if ((uidLen && fnlen) || (!uidLen && !fnlen)) {
2020-11-20 06:18:04 +08:00
PrintAndLogEx(FAILED, "either use option 'u' or option 'f'");
return PM3_EINVARG;
}
2020-12-09 19:18:01 +08:00
int startblock = EM4X50_CONTROL + 1;
em4x50_data_t etd = {.pwd_given = false};
if (pwd_len) {
if (pwd_len != 4) {
PrintAndLogEx(FAILED, "password length must be 4 bytes instead of %d", pwd_len);
2020-11-20 06:18:04 +08:00
return PM3_EINVARG;
} else {
etd.password1 = BYTES2UINT32_BE(pwd);
2020-11-20 06:18:04 +08:00
etd.pwd_given = true;
// if password is available protection and control word can be restored
startblock = EM4X50_PROTECTION;
2020-11-20 06:18:04 +08:00
}
}
2020-12-09 19:18:01 +08:00
if (uidLen) {
PrintAndLogEx(INFO, "Using UID as filename");
char *fptr = filename;
fptr += sprintf(fptr, "lf-4x50-");
FillFileNameByUID(fptr, uid, "-dump", 4);
}
2020-11-20 06:18:04 +08:00
PrintAndLogEx(INFO, "Restoring " _YELLOW_("%s")" to card", filename);
// read data from dump file; file type has to be "bin", "eml" or "json"
2020-12-09 19:18:01 +08:00
uint8_t data[DUMP_FILESIZE] = {0x0};
size_t bytes_read = 0;
2020-11-20 06:18:04 +08:00
if (em4x50_load_file(filename, data, DUMP_FILESIZE, &bytes_read) != PM3_SUCCESS)
return PM3_EFILE;
for (int i = startblock; i < EM4X50_DEVICE_SERIAL; i++) {
PrintAndLogEx(INPLACE, "Restoring block %i", i);
etd.addresses = i << 8 | i;
etd.word = reflect32(BYTES2UINT32_BE((data + 4 * i)));
2020-12-12 21:46:40 +08:00
2020-12-09 19:18:01 +08:00
PacketResponseNG resp;
clearCommandBuffer();
SendCommandNG(CMD_LF_EM4X50_WRITE, (uint8_t *)&etd, sizeof(etd));
2021-02-07 02:59:34 +08:00
if (!WaitForResponseTimeout(CMD_LF_EM4X50_WRITE, &resp, TIMEOUT_CMD)) {
2020-12-09 19:18:01 +08:00
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(WARNING, "Timeout while waiting for reply.");
return PM3_ETIMEOUT;
}
2020-12-09 19:18:01 +08:00
if (resp.status != PM3_SUCCESS) {
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(FAILED, "Restoring data " _RED_("failed"));
return PM3_ESOFT;
}
}
2020-11-20 06:18:04 +08:00
2020-12-12 21:46:40 +08:00
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, "Done");
return PM3_SUCCESS;
}
2020-11-20 06:18:04 +08:00
int CmdEM4x50Sim(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "lf em 4x50 sim",
"Simulates a EM4x50 tag\n"
"First upload to device using `lf em 4x50 eload`",
2021-05-09 04:28:11 +08:00
"lf em 4x50 sim\n"
"lf em 4x50 sim -p 27182818 -> uses password for eload data"
);
void *argtable[] = {
arg_param_begin,
arg_str0("p", "passsword", "<hex>", "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);
uint32_t password = 0;
if (pwd_len) {
if (pwd_len != 4) {
PrintAndLogEx(FAILED, "password length must be 4 bytes, got %d", pwd_len);
return PM3_EINVARG;
} else {
password = BYTES2UINT32_BE(pwd);
}
}
int status = PM3_EFAILED;
2020-12-09 19:18:01 +08:00
PrintAndLogEx(INFO, "Simulating data from emulator memory");
2020-11-20 06:18:04 +08:00
clearCommandBuffer();
SendCommandNG(CMD_LF_EM4X50_SIM, (uint8_t *)&password, sizeof(password));
2020-12-09 19:18:01 +08:00
PacketResponseNG resp;
2021-01-24 05:38:07 +08:00
PrintAndLogEx(INFO, "Press " _GREEN_("<Enter>") " or pm3-button to abort simulation");
bool keypress;
do {
2021-01-16 08:59:02 +08:00
keypress = kbd_enter_pressed();
if (WaitForResponseTimeout(CMD_LF_EM4X50_SIM, &resp, 1500)) {
2021-01-17 02:40:42 +08:00
status = resp.status;
2021-01-16 08:59:02 +08:00
break;
}
} while (keypress == false);
2021-04-19 07:16:39 +08:00
2021-01-16 08:59:02 +08:00
if (keypress) {
SendCommandNG(CMD_BREAK_LOOP, NULL, 0);
2021-01-17 02:40:42 +08:00
status = PM3_EOPABORTED;
2021-01-16 08:59:02 +08:00
}
2021-01-17 02:40:42 +08:00
if ((status == PM3_SUCCESS) || (status == PM3_EOPABORTED))
PrintAndLogEx(INFO, "Done!");
2020-11-21 05:03:19 +08:00
else
PrintAndLogEx(FAILED, "No valid EM4x50 data in memory");
2020-11-21 05:03:19 +08:00
return resp.status;
}
2020-12-04 07:11:57 +08:00
static command_t CommandTable[] = {
2020-12-07 06:08:43 +08:00
{"help", CmdHelp, AlwaysAvailable, "This help"},
2021-06-25 01:27:12 +08:00
{"-----------", CmdHelp, AlwaysAvailable, "--------------------- " _CYAN_("operations") " ---------------------"},
{"brute", CmdEM4x50Brute, IfPm3EM4x50, "Simple bruteforce attack to find password"},
{"chk", CmdEM4x50Chk, IfPm3EM4x50, "Check passwords from dictionary"},
{"dump", CmdEM4x50Dump, IfPm3EM4x50, "Dump EM4x50 tag"},
{"info", CmdEM4x50Info, IfPm3EM4x50, "Tag information"},
{"login", CmdEM4x50Login, IfPm3EM4x50, "Login into EM4x50 tag"},
{"rdbl", CmdEM4x50Read, IfPm3EM4x50, "Read EM4x50 word data"},
{"reader", CmdEM4x50Reader, IfPm3EM4x50, "Show standard read mode data"},
{"restore", CmdEM4x50Restore, IfPm3EM4x50, "Restore EM4x50 dump to tag"},
{"wrbl", CmdEM4x50Write, IfPm3EM4x50, "Write EM4x50 word data"},
{"wrpwd", CmdEM4x50WritePwd, IfPm3EM4x50, "Change EM4x50 password"},
{"wipe", CmdEM4x50Wipe, IfPm3EM4x50, "Wipe EM4x50 tag"},
{"-----------", CmdHelp, AlwaysAvailable, "--------------------- " _CYAN_("simulation") " ---------------------"},
{"eload", CmdEM4x50ELoad, IfPm3EM4x50, "Upload EM4x50 dump to emulator memory"},
{"esave", CmdEM4x50ESave, IfPm3EM4x50, "Save emulator memory to file"},
{"eview", CmdEM4x50EView, IfPm3EM4x50, "View EM4x50 content in emulator memory"},
{"sim", CmdEM4x50Sim, IfPm3EM4x50, "Simulate EM4x50 tag"},
2020-12-04 07:11:57 +08:00
{NULL, NULL, NULL, NULL}
};
static int CmdHelp(const char *Cmd) {
(void)Cmd; // Cmd is not used so far
CmdsHelp(CommandTable);
return PM3_SUCCESS;
}
int CmdLFEM4X50(const char *Cmd) {
clearCommandBuffer();
return CmdsParse(CommandTable, Cmd);
}
2020-12-28 05:22:44 +08:00