diff --git a/client/src/cmdlfem4x.c b/client/src/cmdlfem4x.c index 3ae292848..56681af91 100644 --- a/client/src/cmdlfem4x.c +++ b/client/src/cmdlfem4x.c @@ -664,6 +664,8 @@ static command_t CommandTable[] = { {"4x50_restore",CmdEM4x50Restore, IfPm3EM4x50, "restore EM4x50 dump to tag"}, {"4x50_sim", CmdEM4x50Sim, IfPm3EM4x50, "simulate single EM4x50 word (uid)"}, {"4x50_std_read",CmdEM4x50StdRead, IfPm3EM4x50, "show standard read mode data of EM4x50 tag"}, + {"4x50_eload", CmdEM4x50ELoad, IfPm3EM4x50, "load a binary dump into emulator memory"}, + {"4x50_esave", CmdEM4x50ESave, IfPm3EM4x50, "save emulator memory to file"}, {NULL, NULL, NULL, NULL} }; diff --git a/client/src/cmdlfem4x50.c b/client/src/cmdlfem4x50.c index 0cf298292..7978331da 100644 --- a/client/src/cmdlfem4x50.c +++ b/client/src/cmdlfem4x50.c @@ -187,6 +187,31 @@ static int usage_lf_em4x50_std_read(void) { PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; } +static int usage_lf_em4x50_eload(void) { + PrintAndLogEx(NORMAL, "Load dump file into emulator memory."); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Usage: lf em 4x50_eload [h] f "); + PrintAndLogEx(NORMAL, "Options:"); + PrintAndLogEx(NORMAL, " h - this help"); + PrintAndLogEx(NORMAL, " f - dump filename "); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, _YELLOW_(" lf em 4x50_eload f em4x50dump.bin")); + PrintAndLogEx(NORMAL, ""); + return PM3_SUCCESS; +} +static int usage_lf_em4x50_esave(void) { + PrintAndLogEx(NORMAL, "Save emulator memory to file."); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Usage: lf em 4x50_esave [h] [f ]"); + PrintAndLogEx(NORMAL, "Options:"); + PrintAndLogEx(NORMAL, " h - this help"); + PrintAndLogEx(NORMAL, " f - dump filename "); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, _YELLOW_(" lf em 4x50_esave")); + PrintAndLogEx(NORMAL, _YELLOW_(" lf em 4x50_esave f em4x50dump.bin")); + PrintAndLogEx(NORMAL, ""); + return PM3_SUCCESS; +} static int loadFileEM4x50(const char *filename, uint8_t *data, size_t data_len) { @@ -950,6 +975,7 @@ int CmdEM4x50Watch(const char *Cmd) { int CmdEM4x50Restore(const char *Cmd) { + size_t fn_len = 0; char filename[FILE_PATH_SIZE] = {0}; char szTemp[FILE_PATH_SIZE - 20] = {0x00}; @@ -973,7 +999,9 @@ int CmdEM4x50Restore(const char *Cmd) { break; case 'f': - param_getstr(Cmd, cmdp + 1, filename, FILE_PATH_SIZE); + fn_len = param_getstr(Cmd, cmdp + 1, filename, FILE_PATH_SIZE); + if (fn_len == 0) + errors = true; cmdp += 2; break; @@ -1035,7 +1063,8 @@ int CmdEM4x50Restore(const char *Cmd) { int CmdEM4x50Sim(const char *Cmd) { - bool errors = false, word_given = false;; + bool errors = false, word_given = false, fn_given = false; + size_t fn_len = 0; uint8_t cmdp = 0; char filename[FILE_PATH_SIZE] = {0}; @@ -1055,7 +1084,11 @@ int CmdEM4x50Sim(const char *Cmd) { break; case 'f': - param_getstr(Cmd, cmdp + 1, filename, FILE_PATH_SIZE); + fn_len = param_getstr(Cmd, cmdp + 1, filename, FILE_PATH_SIZE); + if (fn_len != 0) + fn_given = true; + else + errors = true; cmdp += 2; break; @@ -1067,10 +1100,10 @@ int CmdEM4x50Sim(const char *Cmd) { } // validations - if (errors) + if (errors || ((word_given == false) && (fn_given == false))) return usage_lf_em4x50_sim(); - if (word_given && (strlen(filename) != 0)) { + if (word_given && fn_given) { PrintAndLogEx(INFO, "Plesae use either option 'u' or option 'f'"); return usage_lf_em4x50_sim(); } @@ -1167,3 +1200,138 @@ int CmdEM4x50StdRead(const char *Cmd) { return PM3_SUCCESS; } + +static void em4x50_seteml(uint8_t *src, uint32_t offset, uint32_t nobytes) { + + // fast push mode + conn.block_after_ACK = true; + + for (size_t i = offset; i < nobytes; i += PM3_CMD_DATA_SIZE) { + + size_t len = MIN((nobytes - i), PM3_CMD_DATA_SIZE); + if (len == nobytes - i) { + // Disable fast mode on last packet + conn.block_after_ACK = false; + } + + clearCommandBuffer(); + SendCommandOLD(CMD_LF_EM4X50_ESET, i, len, 0, src + i, len); + } +} + +int CmdEM4x50ELoad(const char *Cmd) { + + bool errors = false, fn_given = false; + size_t nobytes = DUMP_FILESIZE, fn_len = 0; + uint8_t cmdp = 0; + char filename[FILE_PATH_SIZE] = {0}; + uint8_t *data = calloc(nobytes, sizeof(uint8_t)); + + if (!data) { + PrintAndLogEx(WARNING, "Fail, cannot allocate memory"); + return PM3_EMALLOC; + } + + while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { + switch (tolower(param_getchar(Cmd, cmdp))) { + + case 'h': + return usage_lf_em4x50_eload(); + break; + + case 'f': + fn_len = param_getstr(Cmd, cmdp + 1, filename, FILE_PATH_SIZE); + if (fn_len != 0) + fn_given = true; + else + errors = true; + cmdp += 2; + break; + + default: + PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); + errors = true; + break; + }; + } + + // validations + if (errors || (fn_given == false)) + return usage_lf_em4x50_eload(); + + // read data from dump file; file type has to be "bin", "eml" or "json" + if (loadFileEM4x50(filename, data, DUMP_FILESIZE) != PM3_SUCCESS) { + PrintAndLogEx(FAILED, "Read error"); + return PM3_EFILE; + } + + PrintAndLogEx(INFO, "Uploading dump " _YELLOW_("%s") " to emulator memory", filename); + em4x50_seteml(data, 0, nobytes); + + free(data); + PrintAndLogEx(SUCCESS, "Done"); + return PM3_SUCCESS; +} + +int CmdEM4x50ESave(const char *Cmd) { + + bool errors = false; + size_t nobytes = DUMP_FILESIZE, fn_len = 0; + uint8_t cmdp = 0; + char filename[FILE_PATH_SIZE] = {0}; + char *fptr = filename; + uint8_t *data = calloc(nobytes, sizeof(uint8_t)); + + if (!data) { + PrintAndLogEx(WARNING, "Fail, cannot allocate memory"); + return PM3_EMALLOC; + } + + while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { + switch (tolower(param_getchar(Cmd, cmdp))) { + + case 'h': + return usage_lf_em4x50_esave(); + break; + + case 'f': + fn_len = param_getstr(Cmd, cmdp + 1, filename, FILE_PATH_SIZE); + if (fn_len == 0) + errors = true; + cmdp += 2; + break; + + default: + PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); + errors = true; + break; + }; + } + + // validations + if (errors) + return usage_lf_em4x50_esave(); + + // download emulator memory + PrintAndLogEx(SUCCESS, "Reading emulator memory..."); + if (!GetFromDevice(BIG_BUF_EML, data, nobytes, 0, NULL, 0, NULL, 2500, false)) { + PrintAndLogEx(WARNING, "Fail, transfer from device time-out"); + free(data); + return PM3_ETIMEOUT; + } + + // user supplied filename? + if (fn_len == 0) { + PrintAndLogEx(INFO, "Using UID as filename"); + fptr += snprintf(fptr, sizeof(filename), "lf-4x50-"); + FillFileNameByUID(fptr, (uint8_t *)&data[4 * EM4X50_DEVICE_ID], "-dump", 4); + } + + PrintAndLogEx(INFO, "Uploading dump " _YELLOW_("%s") " to emulator memory", filename); + em4x50_seteml(data, 0, nobytes); + + saveFile(filename, ".bin", data, nobytes); + saveFileEML(filename, data, nobytes, 4); + saveFileJSON(filename, jsfEM4x50, data, nobytes, NULL); + return PM3_SUCCESS; +} diff --git a/client/src/cmdlfem4x50.h b/client/src/cmdlfem4x50.h index 5873e257e..2c84d9f15 100644 --- a/client/src/cmdlfem4x50.h +++ b/client/src/cmdlfem4x50.h @@ -31,5 +31,7 @@ int CmdEM4x50Watch(const char *Cmd); int CmdEM4x50Restore(const char *Cmd); int CmdEM4x50Sim(const char *Cmd); int CmdEM4x50StdRead(const char *Cmd); +int CmdEM4x50ELoad(const char *Cmd); +int CmdEM4x50ESave(const char *Cmd); #endif diff --git a/include/pm3_cmd.h b/include/pm3_cmd.h index 5f4abb85e..f588f19b3 100644 --- a/include/pm3_cmd.h +++ b/include/pm3_cmd.h @@ -515,6 +515,7 @@ typedef struct { #define CMD_LF_EM4X50_RESTORE 0x0249 #define CMD_LF_EM4X50_SIM 0x0250 #define CMD_LF_EM4X50_STD_READ 0x0251 +#define CMD_LF_EM4X50_ESET 0x0252 // Sampling configuration for LF reader/sniffer #define CMD_LF_SAMPLING_SET_CONFIG 0x021D #define CMD_LF_FSK_SIMULATE 0x021E