Added support for emulating ST25TA tag (IKEA Rothult) with custom NDEF response

This commit is contained in:
Sanduuz
2026-08-28 14:52:30 +03:00
parent a6b4107066
commit 8bc080b3b8
6 changed files with 178 additions and 9 deletions
+7 -1
View File
@@ -26,6 +26,9 @@
#include "bwm_uart_at32.h"
#include "bwm_wifi.h"
#endif
#include "clocks.h"
#include "pm3_cmd.h"
#include "usb_cdc.h"
#include "proxmark3_arm.h"
#include "dbprint.h"
#include "pmflash.h"
@@ -2144,13 +2147,16 @@ static void PacketReceived(PacketCommandNG *packet) {
uint8_t ulauth_1a1[16];
uint8_t ulauth_1a2[16];
bool ulauth_1a2_mirror;
uint16_t st25ta_ndef_len;
uint8_t st25ta_ndef[ST25TA_SIM_NDEF_MAX];
} PACKED;
struct p *payload = (struct p *) packet->data.asBytes;
SimulateIso14443aTagEx(payload->tagtype, payload->flags, payload->uid,
payload->exitAfter, payload->rats, sizeof(payload->rats),
payload->ulauth_1a1, payload->ulauth_1a1_len,
payload->ulauth_1a2, payload->ulauth_1a2_len,
payload->ulauth_1a2_mirror
payload->ulauth_1a2_mirror,
payload->st25ta_ndef, payload->st25ta_ndef_len
); // ## Simulate iso14443a tag - pass tag type & UID
break;
}
+22 -6
View File
@@ -19,6 +19,7 @@
//-----------------------------------------------------------------------------
#include "iso14443a.h"
#include "pm3_cmd.h"
#include "string.h"
#include "proxmark3_arm.h"
#include "cmd.h"
@@ -1714,14 +1715,15 @@ bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data,
// 'hf 14a sim'
//-----------------------------------------------------------------------------
void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *useruid, uint8_t exitAfterNReads) {
SimulateIso14443aTagEx(tagType, flags, useruid, exitAfterNReads, NULL, 0, NULL, 0, NULL, 0, false);
SimulateIso14443aTagEx(tagType, flags, useruid, exitAfterNReads, NULL, 0, NULL, 0, NULL, 0, false, NULL, 0);
}
void SimulateIso14443aTagEx(uint8_t tagType, uint16_t flags, uint8_t *useruid, uint8_t exitAfterNReads,
uint8_t *ats, size_t ats_len,
uint8_t *ulauth_1a1, uint8_t ulauth_1a1_len,
uint8_t *ulauth_1a2, uint8_t ulauth_1a2_len,
bool ulauth_1a2_mirror) {
bool ulauth_1a2_mirror,
const uint8_t *st25ta_ndef, uint16_t st25ta_ndef_len) {
#define ATTACK_KEY_COUNT 16
#define ULC_TAG_NONCE "\x01\x02\x03\x04\x05\x06\x07\x08"
@@ -1754,8 +1756,8 @@ void SimulateIso14443aTagEx(uint8_t tagType, uint16_t flags, uint8_t *useruid, u
// Allocate 512 bytes for the dynamic modulation, created when the reader queries for it
// Such a response is less time critical, so we can prepare them on the fly
#define DYNAMIC_RESPONSE_BUFFER_SIZE 64
#define DYNAMIC_MODULATION_BUFFER_SIZE 512
#define DYNAMIC_RESPONSE_BUFFER_SIZE (ST25TA_SIM_NDEF_MAX + 4)
#define DYNAMIC_MODULATION_BUFFER_SIZE 4096
uint8_t *dynamic_response_buffer = BigBuf_calloc(DYNAMIC_RESPONSE_BUFFER_SIZE);
if (dynamic_response_buffer == NULL) {
@@ -2429,10 +2431,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 != NULL && st25ta_ndef_len > 0 && st25ta_ndef_len <= ST25TA_SIM_NDEF_MAX) {
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;
+2 -1
View File
@@ -148,7 +148,8 @@ void SimulateIso14443aTagEx(uint8_t tagType, uint16_t flags, uint8_t *useruid, u
uint8_t *ats, size_t ats_len,
uint8_t *ulauth_1a1, uint8_t ulauth_1a1_len,
uint8_t *ulauth_1a2, uint8_t ulauth_1a2_len,
bool ulauth_1a2_mirror);
bool ulauth_1a2_mirror,
const uint8_t *st25ta_ndef, uint16_t st25ta_ndef_len);
void SimulateIso14443aTagAID(uint8_t tagType, uint16_t flags, uint8_t *uid,
uint8_t *ats, size_t ats_len, uint8_t *aid, size_t aid_len,
+3
View File
@@ -1037,8 +1037,11 @@ int CmdHF14ASim(const char *Cmd) {
uint8_t ulauth_1a1[16];
uint8_t ulauth_1a2[16];
bool ulauth_1a2_mirror;
uint16_t st25ta_ndef_len;
uint8_t st25ta_ndef[ST25TA_SIM_NDEF_MAX];
} PACKED payload;
memset(&payload, 0x00, sizeof(payload));
payload.tagtype = tagtype;
payload.flags = flags;
payload.exitAfter = exitAfterNReads;
+141 -1
View File
@@ -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 "ui.h"
#include "util.h" // kbd_enter_pressed
#define TIMEOUT 2000
@@ -335,22 +338,122 @@ static int CmdHFST25TAInfo(const char *Cmd) {
return infoHFST25TA();
}
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) {
if (src_len > ST25TA_SIM_NDEF_MAX) {
PrintAndLogEx(ERR, "NDEF response too large. Max %u bytes", ST25TA_SIM_NDEF_MAX);
return PM3_EINVARG;
}
memcpy(dst, src, src_len);
*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_SIM_NDEF_MAX) {
PrintAndLogEx(ERR, "NDEF response too large. Max %u bytes", ST25TA_SIM_NDEF_MAX);
return PM3_EINVARG;
}
memcpy(dst, src, src_len);
dst[src_len] = 0x90;
dst[src_len + 1] = 0x00;
*dst_len = src_len + 2;
return PM3_SUCCESS;
}
}
if (src_len + 4 > ST25TA_SIM_NDEF_MAX) {
PrintAndLogEx(ERR, "NDEF response too large. Max %u bytes", ST25TA_SIM_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_simulate_with_ndef(const uint8_t *uid, const uint8_t *ndef, uint16_t ndef_len) {
struct {
uint8_t tagtype;
uint16_t flags;
uint8_t uid[10];
uint8_t exitAfter;
uint8_t rats[20];
uint8_t ulauth_1a1_len;
uint8_t ulauth_1a2_len;
uint8_t ulauth_1a1[16];
uint8_t ulauth_1a2[16];
bool ulauth_1a2_mirror;
uint16_t st25ta_ndef_len;
uint8_t st25ta_ndef[ST25TA_SIM_NDEF_MAX];
} PACKED payload;
memset(&payload, 0x00, sizeof(payload));
payload.tagtype = 10;
payload.flags = 0;
FLAG_SET_UID_IN_DATA(payload.flags, 7);
memcpy(payload.uid, uid, 7);
if (ndef != NULL && ndef_len > 0) {
payload.st25ta_ndef_len = ndef_len;
memcpy(payload.st25ta_ndef, ndef, ndef_len);
}
clearCommandBuffer();
SendCommandNG(CMD_HF_ISO14443A_SIMULATE, (uint8_t *)&payload, sizeof(payload));
PrintAndLogEx(INFO, "Press " _GREEN_("pm3 button") " or " _GREEN_("<Enter>") " to abort simulation");
PacketResponseNG resp = {0};
bool keypress = kbd_enter_pressed();
while (keypress == false) {
keypress = kbd_enter_pressed();
if (WaitForResponseTimeout(CMD_HF_MIFARE_SIMULATE, &resp, 1500) == false) {
continue;
}
break;
}
return resp.status;
}
static int CmdHFST25TASim(const char *Cmd) {
int uidlen = 0;
uint8_t uid[7] = {0};
int ndef_hex_len = 0;
uint8_t ndef_hex[ST25TA_SIM_NDEF_MAX] = {0};
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf st25ta sim",
"Emulating ST25TA512B tag with 7 byte UID",
"hf st25ta sim -u 02E2007D0FCA4C\n");
"hf st25ta sim -u 02E2007D0FCA4C\n"
"hf st25ta sim -u 02E2007D0FCA4C --ndef 001BD1011754027A68A234CBD0E203C73E620BE8C63C852CC5313131329000\n"
"hf st25ta sim -u 02E2007D0FCA4C -f my-ndef.bin\n");
void *argtable[] = {
arg_param_begin,
arg_str1("u", "uid", "<hex>", "7 byte UID"),
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 != 7) {
@@ -358,6 +461,43 @@ static int CmdHFST25TASim(const char *Cmd) {
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 either --ndef or -f, not both");
return PM3_EINVARG;
}
uint8_t ndef[ST25TA_SIM_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 if (fnlen > 0) {
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;
}
}
if (ndef_len > 0) {
PrintAndLogEx(SUCCESS, "Emulating ST25TA512B with supplied NDEF response (%u bytes)", ndef_len);
return st25ta_simulate_with_ndef(uid, ndef, ndef_len);
}
char param[40];
snprintf(param, sizeof(param), "-t 10 -u %s", sprint_hex_inrow(uid, uidlen));
return CmdHF14ASim(param);
+3
View File
@@ -974,6 +974,9 @@ typedef struct {
#define CMD_UNKNOWN 0xFFFF
// Max NDEF record length for ST25TA simulation
#define ST25TA_SIM_NDEF_MAX 256
// Mifare simulation flags
// In interactive mode, we are expected to finish the operation with an ACK
#define FLAG_INTERACTIVE 0x0001