mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2026-09-11 18:29:22 -07:00
Merge pull request #3427 from Sanduuz/feature/st25ta_ndef_sim
Added support for emulating ST25TA tag (IKEA Rothult) with custom NDEF response
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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"
|
||||
|
||||
+43
-5
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
+378
-4
@@ -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", "<hex>", "optional 7 byte UID to store for simulation"),
|
||||
arg_str0(NULL, "ndef", "<hex>", "NDEF data or full READ BINARY response"),
|
||||
arg_str0("f", "file", "<fn>", "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", "<fn>", "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", "<hex>", "7 byte UID"),
|
||||
arg_str0("u", "uid", "<hex>", "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 <uid>`") " or " _YELLOW_("`hf st25ta eload -u <uid> ...`"));
|
||||
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"},
|
||||
|
||||
+51
-5
@@ -10231,13 +10231,58 @@
|
||||
],
|
||||
"usage": "hf sniff [-h] [--sp <dec>] [--st <dec>] [--smode [none|drop|min|max|avg]] [--sratio <dec, ms>]"
|
||||
},
|
||||
"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 <fn> Specify a filename for dump file"
|
||||
],
|
||||
"usage": "hf st25ta esave [-h] [-f <fn>]"
|
||||
},
|
||||
"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 <hex> optional 7 byte UID to store for simulation",
|
||||
"--ndef <hex> NDEF data or full READ BINARY response",
|
||||
"-f, --file <fn> load NDEF data or full READ BINARY response from file"
|
||||
],
|
||||
"usage": "hf st25ta eload [-h] [-u <hex>] [--ndef <hex>] [-f <fn>]"
|
||||
},
|
||||
"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 <hex> 7 byte UID"
|
||||
],
|
||||
"usage": "hf st25ta sim [-h] -u <hex>"
|
||||
"usage": "hf st25ta sim [-h] [-u <hex>]"
|
||||
},
|
||||
"hf tesla help": {
|
||||
"command": "hf tesla help",
|
||||
|
||||
@@ -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`
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user