diff --git a/CHANGELOG.md b/CHANGELOG.md index 2f18b6b49..ba79312c4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -81,6 +81,7 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac - Fixed `hf_mf_uscuid_prog.lua` - corrected the script name shown in its usage text (@c-barron) - Added `hf felica seacauth1` command (@kormax) - Added `lf trovan` commands to support Trovan Animal ID (@iceman1001) +- Added support for emulating ST25TA tag (IKEA Rothult) with custom NDEF response (@sanduuz) - Added `hf mf gdmgetblk/gdmgethidblk/gdmsethidblk/gdmsetuid/gdmwipe/gdmsetsig` (@0x6r1an0y) - Changed `hf mf gdmparsecfg/gdmsetblk` (@0x6r1an0y) - Renamed `hf mf gdmcfg` (@0x6r1an0y) diff --git a/armsrc/appmain.c b/armsrc/appmain.c index 7892d0841..328434b14 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -27,6 +27,7 @@ #include "bwm_forward.h" // bwm_fwd_negotiate_baud() #include "bwm_wifi.h" #endif +#include "pm3_cmd.h" #include "proxmark3_arm.h" #include "dbprint.h" #include "pmflash.h" diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index 50b241ef2..bb5bfbd47 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -19,6 +19,7 @@ //----------------------------------------------------------------------------- #include "iso14443a.h" +#include "pm3_cmd.h" #include "string.h" #include "proxmark3_arm.h" #include "cmd.h" @@ -1776,13 +1777,20 @@ void SimulateIso14443aTagEx(uint8_t tagType, uint16_t flags, uint8_t *useruid, u #define DYNAMIC_RESPONSE_BUFFER_SIZE 64 #define DYNAMIC_MODULATION_BUFFER_SIZE 512 - uint8_t *dynamic_response_buffer = BigBuf_calloc(DYNAMIC_RESPONSE_BUFFER_SIZE); + uint16_t dynamic_response_buffer_size = DYNAMIC_RESPONSE_BUFFER_SIZE; + uint16_t dynamic_modulation_buffer_size = DYNAMIC_MODULATION_BUFFER_SIZE; + if (tagType == 10) { + dynamic_response_buffer_size = ST25TA_EML_NDEF_MAX + 4; + dynamic_modulation_buffer_size = 4096; + } + + uint8_t *dynamic_response_buffer = BigBuf_calloc(dynamic_response_buffer_size); if (dynamic_response_buffer == NULL) { BigBuf_free_keep_EM(); reply_ng(CMD_HF_MIFARE_SIMULATE, PM3_EMALLOC, NULL, 0); return; } - uint8_t *dynamic_modulation_buffer = BigBuf_calloc(DYNAMIC_MODULATION_BUFFER_SIZE); + uint8_t *dynamic_modulation_buffer = BigBuf_calloc(dynamic_modulation_buffer_size); if (dynamic_modulation_buffer == NULL) { BigBuf_free_keep_EM(); reply_ng(CMD_HF_MIFARE_SIMULATE, PM3_EMALLOC, NULL, 0); @@ -1803,6 +1811,22 @@ void SimulateIso14443aTagEx(uint8_t tagType, uint16_t flags, uint8_t *useruid, u return; } + uint8_t st25ta_ndef[ST25TA_EML_NDEF_MAX] = {0}; + uint16_t st25ta_ndef_len = 0; + if (tagType == 10) { + uint8_t st25ta_header[ST25TA_EML_DATA_OFFSET] = {0}; + emlGet(st25ta_header, 0, sizeof(st25ta_header)); + if (memcmp(st25ta_header + ST25TA_EML_MAGIC_OFFSET, ST25TA_EML_MAGIC, 4) == 0) { + st25ta_ndef_len = st25ta_header[ST25TA_EML_LEN_OFFSET] | + (st25ta_header[ST25TA_EML_LEN_OFFSET + 1] << 8); + if (st25ta_ndef_len <= ST25TA_EML_NDEF_MAX) { + emlGet(st25ta_ndef, ST25TA_EML_DATA_OFFSET, st25ta_ndef_len); + } else { + st25ta_ndef_len = 0; + } + } + } + mfu_dump_t *mfu_em_dump = NULL; if (tagType == 2 || tagType == 7) { mfu_em_dump = (mfu_dump_t *)BigBuf_get_EM_addr(); @@ -2448,10 +2472,24 @@ void SimulateIso14443aTagEx(uint8_t tagType, uint16_t flags, uint8_t *useruid, u if (tagType == 10) { // we replay 90 00 for all commands but the read bin and we deny the verify cmd. + static const uint8_t default_st25ta_ndef[] = { + 0x00, 0x1b, 0xd1, 0x01, 0x17, 0x54, 0x02, 0x7a, + 0x68, 0xa2, 0x34, 0xcb, 0xd0, 0xe2, 0x03, 0xc7, + 0x3e, 0x62, 0x0b, 0xe8, 0xc6, 0x3c, 0x85, 0x2c, + 0xc5, 0x31, 0x31, 0x31, 0x32, 0x90, 0x00 + }; + const uint8_t *ndef = default_st25ta_ndef; + uint16_t ndef_len = sizeof(default_st25ta_ndef); + + if (st25ta_ndef_len > 0) { + ndef = st25ta_ndef; + ndef_len = st25ta_ndef_len; + } + if (memcmp("\x02\xa2\xb0\x00\x00\x1d\x51\x69", receivedCmd, 8) == 0) { dynamic_response_info.response[0] = receivedCmd[0]; - memcpy(dynamic_response_info.response + 1, "\x00\x1b\xd1\x01\x17\x54\x02\x7a\x68\xa2\x34\xcb\xd0\xe2\x03\xc7\x3e\x62\x0b\xe8\xc6\x3c\x85\x2c\xc5\x31\x31\x31\x32\x90\x00", 31); - dynamic_response_info.response_n = 32; + memcpy(dynamic_response_info.response + 1, ndef, ndef_len); + dynamic_response_info.response_n = ndef_len + 1; } else if (memcmp("\x02\x00\x20\x00\x01\x00\x6e\xa9", receivedCmd, 8) == 0) { dynamic_response_info.response[0] = receivedCmd[0]; dynamic_response_info.response[1] = 0x63; @@ -2546,7 +2584,7 @@ void SimulateIso14443aTagEx(uint8_t tagType, uint16_t flags, uint8_t *useruid, u AddCrc14A(dynamic_response_info.response, dynamic_response_info.response_n); dynamic_response_info.response_n += 2; - if (prepare_tag_modulation(&dynamic_response_info, DYNAMIC_MODULATION_BUFFER_SIZE) == false) { + if (prepare_tag_modulation(&dynamic_response_info, dynamic_modulation_buffer_size) == false) { if (g_dbglevel >= DBG_DEBUG) DbpString("Error preparing tag response"); LogTrace(receivedCmd, Uart.len, Uart.startTime * 16 - DELAY_AIR2ARM_AS_TAG, Uart.endTime * 16 - DELAY_AIR2ARM_AS_TAG, Uart.parity, true); break; diff --git a/client/src/cmdhf14a.c b/client/src/cmdhf14a.c index 6159e846c..f798cbaba 100644 --- a/client/src/cmdhf14a.c +++ b/client/src/cmdhf14a.c @@ -1106,6 +1106,7 @@ int CmdHF14ASim(const char *Cmd) { bool ulauth_1a2_mirror; } PACKED payload; + memset(&payload, 0x00, sizeof(payload)); payload.tagtype = tagtype; payload.flags = flags; payload.exitAfter = exitAfterNReads; diff --git a/client/src/cmdhfst25ta.c b/client/src/cmdhfst25ta.c index c859f5c05..a34d8d086 100644 --- a/client/src/cmdhfst25ta.c +++ b/client/src/cmdhfst25ta.c @@ -26,6 +26,7 @@ #include "cliparser.h" #include "crc16.h" #include "cmdhf14a.h" +#include "pm3_cmd.h" #include "protocols.h" // definitions of ISO14A/7816 protocol #include "iso7816/apduinfo.h" // GetAPDUCodeDescription #include "nfc/ndef.h" // NDEFRecordsDecodeAndPrint @@ -34,6 +35,8 @@ #include "protocols.h" // ISO7816 APDU return codes #include "crypto/libpcrypto.h" // ecdsa #include "crypto/originality.h" +#include "mifare/mifarehost.h" // mf_eml_set_mem_xt / mf_eml_get_mem_xt +#include "ui.h" #define TIMEOUT 2000 @@ -335,29 +338,397 @@ static int CmdHFST25TAInfo(const char *Cmd) { return infoHFST25TA(); } +static int st25ta_ndef_records_len(const uint8_t *data, size_t data_len, size_t *records_len) { + if (data == NULL || records_len == NULL || data_len < 3) { + return PM3_EINVARG; + } + + size_t offset = 0; + for (;;) { + if (offset + 3 > data_len) { + return PM3_ESOFT; + } + + uint8_t flags = data[offset]; + uint8_t type_len = data[offset + 1]; + bool short_record = (flags & 0x10) != 0; + bool id_length_present = (flags & 0x08) != 0; + bool message_end = (flags & 0x40) != 0; + size_t header_len = short_record ? 3 : 6; + uint32_t payload_len = 0; + + if (short_record) { + payload_len = data[offset + 2]; + } else { + if (offset + 6 > data_len) { + return PM3_ESOFT; + } + payload_len = (data[offset + 2] << 24) | + (data[offset + 3] << 16) | + (data[offset + 4] << 8) | + data[offset + 5]; + } + + uint8_t id_len = 0; + if (id_length_present) { + if (offset + header_len >= data_len) { + return PM3_ESOFT; + } + id_len = data[offset + header_len]; + header_len++; + } + + size_t record_len = header_len + type_len + id_len + payload_len; + if (record_len == 0 || offset + record_len > data_len) { + return PM3_ESOFT; + } + + offset += record_len; + if (message_end) { + *records_len = offset; + return PM3_SUCCESS; + } + } +} + +static int st25ta_normalize_sim_ndef(const uint8_t *src, size_t src_len, uint8_t *dst, uint16_t *dst_len) { + if (src == NULL || src_len == 0 || dst == NULL || dst_len == NULL) { + return PM3_EINVARG; + } + + if (src_len >= 4 && src[src_len - 2] == 0x90 && src[src_len - 1] == 0x00) { + uint16_t nlen = (src[0] << 8) | src[1]; + if (nlen + 4 != src_len) { + nlen = src_len - 4; + } + + if (src_len > ST25TA_EML_NDEF_MAX) { + PrintAndLogEx(ERR, "NDEF response too large. Max %u bytes", ST25TA_EML_NDEF_MAX); + return PM3_EINVARG; + } + + dst[0] = (nlen >> 8) & 0xff; + dst[1] = nlen & 0xff; + memcpy(dst + 2, src + 2, src_len - 4); + dst[src_len - 2] = 0x90; + dst[src_len - 1] = 0x00; + *dst_len = src_len; + return PM3_SUCCESS; + } + + if (src_len >= 2) { + uint16_t nlen = (src[0] << 8) | src[1]; + if (nlen + 2 == src_len) { + if (src_len + 2 > ST25TA_EML_NDEF_MAX) { + PrintAndLogEx(ERR, "NDEF response too large. Max %u bytes", ST25TA_EML_NDEF_MAX); + return PM3_EINVARG; + } + + dst[0] = (nlen >> 8) & 0xff; + dst[1] = nlen & 0xff; + memcpy(dst + 2, src + 2, nlen); + dst[src_len] = 0x90; + dst[src_len + 1] = 0x00; + *dst_len = src_len + 2; + return PM3_SUCCESS; + } + } + + if (src_len + 4 > ST25TA_EML_NDEF_MAX) { + PrintAndLogEx(ERR, "NDEF response too large. Max %u bytes", ST25TA_EML_NDEF_MAX); + return PM3_EINVARG; + } + + dst[0] = (src_len >> 8) & 0xff; + dst[1] = src_len & 0xff; + memcpy(dst + 2, src, src_len); + dst[src_len + 2] = 0x90; + dst[src_len + 3] = 0x00; + *dst_len = src_len + 4; + return PM3_SUCCESS; +} + +static int st25ta_upload_sim_data(const uint8_t *uid, uint8_t uid_len, const uint8_t *ndef, uint16_t ndef_len) { + uint8_t data[ST25TA_EML_DATA_OFFSET + ST25TA_EML_NDEF_MAX] = {0}; + uint16_t data_len = ST25TA_EML_DATA_OFFSET + ndef_len; + memcpy(data + ST25TA_EML_MAGIC_OFFSET, ST25TA_EML_MAGIC, 4); + data[ST25TA_EML_LEN_OFFSET] = ndef_len & 0xff; + data[ST25TA_EML_LEN_OFFSET + 1] = (ndef_len >> 8) & 0xff; + data[ST25TA_EML_UIDLEN_OFFSET] = uid_len; + if (uid_len == 7) { + memcpy(data + ST25TA_EML_UID_OFFSET, uid, uid_len); + } + memcpy(data + ST25TA_EML_DATA_OFFSET, ndef, ndef_len); + + for (uint16_t offset = 0; offset < data_len;) { + uint8_t chunk_len = MIN(data_len - offset, UINT8_MAX); + int res = mf_eml_set_mem_xt(data + offset, offset, chunk_len, 1); + if (res != PM3_SUCCESS) { + return res; + } + offset += chunk_len; + } + + return PM3_SUCCESS; +} + +static int st25ta_download_sim_data(uint8_t *uid, uint8_t *uid_len, uint8_t *ndef, uint16_t *ndef_len) { + uint8_t header[ST25TA_EML_DATA_OFFSET] = {0}; + int res = mf_eml_get_mem_xt(header, 0, sizeof(header), 1); + if (res != PM3_SUCCESS) { + return res; + } + + if (memcmp(header + ST25TA_EML_MAGIC_OFFSET, ST25TA_EML_MAGIC, 4) != 0) { + PrintAndLogEx(ERR, "No ST25TA data loaded in emulator memory"); + return PM3_ESOFT; + } + + uint16_t len = header[ST25TA_EML_LEN_OFFSET] | + (header[ST25TA_EML_LEN_OFFSET + 1] << 8); + if (len == 0 || len > ST25TA_EML_NDEF_MAX) { + PrintAndLogEx(ERR, "Invalid ST25TA NDEF response length in emulator memory: %u", len); + return PM3_ESOFT; + } + + if (uid_len != NULL) { + *uid_len = header[ST25TA_EML_UIDLEN_OFFSET]; + } + if (uid != NULL && header[ST25TA_EML_UIDLEN_OFFSET] == 7) { + memcpy(uid, header + ST25TA_EML_UID_OFFSET, 7); + } + + for (uint16_t offset = 0; offset < len;) { + uint8_t chunk_len = MIN(len - offset, UINT8_MAX); + res = mf_eml_get_mem_xt(ndef + offset, ST25TA_EML_DATA_OFFSET + offset, chunk_len, 1); + if (res != PM3_SUCCESS) { + return res; + } + offset += chunk_len; + } + + *ndef_len = len; + return PM3_SUCCESS; +} + +static int CmdHFST25TAELoad(const char *Cmd) { + int uidlen = 0; + uint8_t uid[7] = {0}; + int ndef_hex_len = 0; + uint8_t ndef_hex[ST25TA_EML_NDEF_MAX] = {0}; + + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf st25ta eload", + "Load ST25TA NDEF response into emulator memory", + "hf st25ta eload --ndef 001BD1011754027A68A234CBD0E203C73E620BE8C63C852CC5313131329000\n" + "hf st25ta eload -f my-ndef.bin\n" + "hf st25ta eload -u 02E2007D0FCA4C -f my-ndef.bin\n"); + + void *argtable[] = { + arg_param_begin, + arg_str0("u", "uid", "", "optional 7 byte UID to store for simulation"), + arg_str0(NULL, "ndef", "", "NDEF data or full READ BINARY response"), + arg_str0("f", "file", "", "load NDEF data or full READ BINARY response from file"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + CLIGetHexWithReturn(ctx, 1, uid, &uidlen); + int ndef_res = CLIParamHexToBuf(arg_get_str(ctx, 2), ndef_hex, sizeof(ndef_hex), &ndef_hex_len); + int fnlen = 0; + char filename[FILE_PATH_SIZE] = {0}; + CLIParamStrToBuf(arg_get_str(ctx, 3), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); + CLIParserFree(ctx); + + if (uidlen != 0 && uidlen != 7) { + PrintAndLogEx(ERR, "UID must be 7 hex bytes"); + return PM3_EINVARG; + } + + if (ndef_res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Invalid NDEF hex"); + return ndef_res; + } + + if ((ndef_hex_len > 0) == (fnlen > 0)) { + PrintAndLogEx(ERR, "Use exactly one of --ndef or -f"); + return PM3_EINVARG; + } + + uint8_t ndef[ST25TA_EML_NDEF_MAX] = {0}; + uint16_t ndef_len = 0; + if (ndef_hex_len > 0) { + int res = st25ta_normalize_sim_ndef(ndef_hex, ndef_hex_len, ndef, &ndef_len); + if (res != PM3_SUCCESS) { + return res; + } + } else { + uint8_t *file_data = NULL; + size_t file_len = 0; + int res = loadFile_safe(filename, "", (void **)&file_data, &file_len); + if (res != PM3_SUCCESS) { + return res; + } + res = st25ta_normalize_sim_ndef(file_data, file_len, ndef, &ndef_len); + free(file_data); + if (res != PM3_SUCCESS) { + return res; + } + } + + int res = st25ta_upload_sim_data(uid, uidlen, ndef, ndef_len); + if (res != PM3_SUCCESS) { + PrintAndLogEx(FAILED, "Failed uploading ST25TA data to emulator memory"); + return res; + } + + PrintAndLogEx(SUCCESS, "Uploaded ST25TA NDEF response to emulator memory (" _YELLOW_("%u") " bytes)", ndef_len); + if (uidlen == 7) { + PrintAndLogEx(SUCCESS, "Stored UID " _YELLOW_("%s"), sprint_hex_inrow(uid, uidlen)); + } + PrintAndLogEx(HINT, "Hint: Try " _YELLOW_("`hf st25ta sim`")); + return PM3_SUCCESS; +} + +static int CmdHFST25TAEView(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf st25ta eview", + "Display ST25TA emulator memory", + "hf st25ta eview\n" + "hf st25ta eview -v\n"); + + void *argtable[] = { + arg_param_begin, + arg_lit0("v", "verbose", "verbose NDEF output"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + bool verbose = arg_get_lit(ctx, 1); + CLIParserFree(ctx); + + uint8_t uid[7] = {0}; + uint8_t uid_len = 0; + uint8_t ndef[ST25TA_EML_NDEF_MAX] = {0}; + uint16_t ndef_len = 0; + int res = st25ta_download_sim_data(uid, &uid_len, ndef, &ndef_len); + if (res != PM3_SUCCESS) { + return res; + } + + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "--- " _CYAN_("ST25TA emulator memory") " ----------------"); + if (uid_len == 7) { + PrintAndLogEx(INFO, "UID................ " _YELLOW_("%s"), sprint_hex_inrow(uid, uid_len)); + } else { + PrintAndLogEx(INFO, "UID................ " _YELLOW_("not loaded")); + } + PrintAndLogEx(INFO, "NDEF response len.. " _YELLOW_("%u") " bytes", ndef_len); + PrintAndLogEx(INFO, "NDEF response raw:"); + print_buffer(ndef, ndef_len, 1); + + if (ndef_len >= 4 && ndef[ndef_len - 2] == 0x90 && ndef[ndef_len - 1] == 0x00) { + uint16_t nlen = (ndef[0] << 8) | ndef[1]; + size_t records_len = ndef_len - 4; + if (nlen <= records_len) { + records_len = nlen; + } + + size_t calculated_len = 0; + if (st25ta_ndef_records_len(ndef + 2, records_len, &calculated_len) == PM3_SUCCESS && + calculated_len < records_len) { + records_len = calculated_len; + } + + PrintAndLogEx(INFO, "--- " _CYAN_("NDEF records") " --------------------------"); + NDEFRecordsDecodeAndPrint(ndef + 2, records_len, verbose); + } + + return PM3_SUCCESS; +} + +static int CmdHFST25TAESave(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf st25ta esave", + "Save ST25TA emulator memory NDEF response to file", + "hf st25ta esave\n" + "hf st25ta esave -f hf-st25ta-ndef\n"); + + void *argtable[] = { + arg_param_begin, + arg_str0("f", "file", "", "Specify a filename for dump file"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + char filename[FILE_PATH_SIZE] = {0}; + int fnlen = 0; + CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); + CLIParserFree(ctx); + + if (fnlen == 0) { + snprintf(filename, sizeof(filename), "hf-st25ta-ndef"); + } + + uint8_t ndef[ST25TA_EML_NDEF_MAX] = {0}; + uint16_t ndef_len = 0; + int res = st25ta_download_sim_data(NULL, NULL, ndef, &ndef_len); + if (res != PM3_SUCCESS) { + return res; + } + + if (ndef_len < 4 || ndef[ndef_len - 2] != 0x90 || ndef[ndef_len - 1] != 0x00) { + PrintAndLogEx(ERR, "Loaded ST25TA data is not a READ BINARY response ending in 9000"); + return PM3_ESOFT; + } + + uint16_t nlen = (ndef[0] << 8) | ndef[1]; + if (nlen == 0 || nlen + 4 > ndef_len) { + PrintAndLogEx(ERR, "Invalid ST25TA NLEN in emulator memory: %u", nlen); + return PM3_ESOFT; + } + + return pm3_save_dump(filename, ndef + 2, nlen, jsfNDEF); +} + static int CmdHFST25TASim(const char *Cmd) { int uidlen = 0; uint8_t uid[7] = {0}; CLIParserContext *ctx; CLIParserInit(&ctx, "hf st25ta sim", - "Emulating ST25TA512B tag with 7 byte UID", - "hf st25ta sim -u 02E2007D0FCA4C\n"); + "Emulating ST25TA512B tag. Uses NDEF data from `hf st25ta eload` if loaded.", + "hf st25ta sim -u 02E2007D0FCA4C\n" + "hf st25ta sim\n"); void *argtable[] = { arg_param_begin, - arg_str1("u", "uid", "", "7 byte UID"), + arg_str0("u", "uid", "", "7 byte UID"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); CLIGetHexWithReturn(ctx, 1, uid, &uidlen); CLIParserFree(ctx); - if (uidlen != 7) { + if (uidlen != 0 && uidlen != 7) { PrintAndLogEx(ERR, "UID must be 7 hex bytes"); return PM3_EINVARG; } + if (uidlen == 0) { + uint8_t header[ST25TA_EML_DATA_OFFSET] = {0}; + int res = mf_eml_get_mem_xt(header, 0, sizeof(header), 1); + if (res != PM3_SUCCESS) { + return res; + } + if (memcmp(header + ST25TA_EML_MAGIC_OFFSET, ST25TA_EML_MAGIC, 4) == 0 && + header[ST25TA_EML_UIDLEN_OFFSET] == 7) { + memcpy(uid, header + ST25TA_EML_UID_OFFSET, sizeof(uid)); + uidlen = sizeof(uid); + } else { + PrintAndLogEx(ERR, "No UID supplied and no UID loaded. Use " _YELLOW_("`hf st25ta sim -u `") " or " _YELLOW_("`hf st25ta eload -u ...`")); + return PM3_EINVARG; + } + } + char param[40]; snprintf(param, sizeof(param), "-t 10 -u %s", sprint_hex_inrow(uid, uidlen)); return CmdHF14ASim(param); @@ -811,6 +1182,9 @@ static int CmdHFST25TAList(const char *Cmd) { static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help"}, + {"eload", CmdHFST25TAELoad, IfPm3Iso14443a, "Upload NDEF response into emulator memory"}, + {"esave", CmdHFST25TAESave, IfPm3Iso14443a, "Save emulator memory to file"}, + {"eview", CmdHFST25TAEView, IfPm3Iso14443a, "View emulator memory"}, {"info", CmdHFST25TAInfo, IfPm3Iso14443a, "Tag information"}, {"list", CmdHFST25TAList, AlwaysAvailable, "List ISO 14443A/7816 history"}, {"ndefread", CmdHFST25TANdefRead, AlwaysAvailable, "read NDEF file on tag"}, diff --git a/doc/commands.json b/doc/commands.json index f335c5800..7a7c8b627 100644 --- a/doc/commands.json +++ b/doc/commands.json @@ -10231,13 +10231,58 @@ ], "usage": "hf sniff [-h] [--sp ] [--st ] [--smode [none|drop|min|max|avg]] [--sratio ]" }, + "hf st25ta esave": { + "command": "hf st25ta esave", + "description": "Save ST25TA emulator memory NDEF response to file", + "notes": [ + "hf st25ta esave", + "hf st25ta esave -f hf-st25ta-ndef" + ], + "offline": false, + "options": [ + "-h, --help This help", + "-f, --file Specify a filename for dump file" + ], + "usage": "hf st25ta esave [-h] [-f ]" + }, + "hf st25ta eview": { + "command": "hf st25ta eview", + "description": "Display ST25TA emulator memory", + "notes": [ + "hf st25ta eview", + "hf st25ta eview -v" + ], + "offline": false, + "options": [ + "-h, --help This help", + "-v, --verbose verbose NDEF output" + ], + "usage": "hf st25ta eview [-hv]" + }, "hf st25ta help": { "command": "hf st25ta help", - "description": "help This help list List ISO 14443A/7816 history ndefread read NDEF file on tag --------------------------------------------------------------------------------------- hf st25ta info available offline: no Get info about ST25TA tag", + "description": "help This help list List ISO 14443A/7816 history ndefread read NDEF file on tag --------------------------------------------------------------------------------------- hf st25ta eload available offline: no Load ST25TA NDEF response into emulator memory", + "notes": [ + "hf st25ta eload --ndef 001BD1011754027A68A234CBD0E203C73E620BE8C63C852CC5313131329000", + "hf st25ta eload -f my-ndef.bin", + "hf st25ta eload -u 02E2007D0FCA4C -f my-ndef.bin" + ], + "offline": true, + "options": [ + "-h, --help This help", + "-u, --uid optional 7 byte UID to store for simulation", + "--ndef NDEF data or full READ BINARY response", + "-f, --file load NDEF data or full READ BINARY response from file" + ], + "usage": "hf st25ta eload [-h] [-u ] [--ndef ] [-f ]" + }, + "hf st25ta info": { + "command": "hf st25ta info", + "description": "Get info about ST25TA tag", "notes": [ "hf st25ta info" ], - "offline": true, + "offline": false, "options": [ "-h, --help This help" ], @@ -10317,16 +10362,17 @@ }, "hf st25ta sim": { "command": "hf st25ta sim", - "description": "Emulating ST25TA512B tag with 7 byte UID", + "description": "Emulating ST25TA512B tag. Uses NDEF data from `hf st25ta eload` if loaded.", "notes": [ - "hf st25ta sim -u 02E2007D0FCA4C" + "hf st25ta sim -u 02E2007D0FCA4C", + "hf st25ta sim" ], "offline": false, "options": [ "-h, --help This help", "-u, --uid 7 byte UID" ], - "usage": "hf st25ta sim [-h] -u " + "usage": "hf st25ta sim [-h] [-u ]" }, "hf tesla help": { "command": "hf tesla help", diff --git a/doc/commands.md b/doc/commands.md index d63123ca1..79b30d2b7 100644 --- a/doc/commands.md +++ b/doc/commands.md @@ -896,6 +896,9 @@ Check column "offline" for their availability. |command |offline |description |------- |------- |----------- |`hf st25ta help `|Y |`This help` +|`hf st25ta eload `|N |`Upload NDEF response into emulator memory` +|`hf st25ta esave `|N |`Save emulator memory to file` +|`hf st25ta eview `|N |`View emulator memory` |`hf st25ta info `|N |`Tag information` |`hf st25ta list `|Y |`List ISO 14443A/7816 history` |`hf st25ta ndefread `|Y |`read NDEF file on tag` diff --git a/include/pm3_cmd.h b/include/pm3_cmd.h index 19312601e..56f643c35 100644 --- a/include/pm3_cmd.h +++ b/include/pm3_cmd.h @@ -1209,6 +1209,16 @@ typedef struct { #define CMD_UNKNOWN 0xFFFF +// ST25TA simulation data in emulator memory: +// magic[4] || len_le[2] || uid_len[1] || uid[7] || READ BINARY response bytes +#define ST25TA_EML_MAGIC_OFFSET 0 +#define ST25TA_EML_LEN_OFFSET 4 +#define ST25TA_EML_UIDLEN_OFFSET 6 +#define ST25TA_EML_UID_OFFSET 7 +#define ST25TA_EML_DATA_OFFSET 14 +#define ST25TA_EML_NDEF_MAX 256 +#define ST25TA_EML_MAGIC "ST25" + // Mifare simulation flags // In interactive mode, we are expected to finish the operation with an ACK #define FLAG_INTERACTIVE 0x0001