mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2026-10-23 10:53:06 +00:00
Merge pull request #3653 from 0x6r1an0y/20260917-15magic-fix
Improve `hf 15 csetuid` command
This commit is contained in:
8 files changed
+433
-169
No files matched your search
@@ -2048,6 +2048,18 @@ static void PacketReceived(PacketCommandNG *packet) {
|
||||
SetTag15693Uid_v2(payload->uid);
|
||||
break;
|
||||
}
|
||||
case CMD_HF_ISO15693_CSETUID_V3: {
|
||||
struct p {
|
||||
uint8_t uid[8];
|
||||
} PACKED;
|
||||
struct p *payload = (struct p *) packet->data.asBytes;
|
||||
SetTag15693Uid_v3(payload->uid);
|
||||
break;
|
||||
}
|
||||
case CMD_HF_ISO15693_CFINALIZE_V3: {
|
||||
FinalizeTag15693_v3();
|
||||
break;
|
||||
}
|
||||
case CMD_HF_ISO15693_SLIX_DISABLE_EAS: {
|
||||
struct p {
|
||||
uint8_t pwd[4];
|
||||
|
||||
+306
-35
@@ -3202,51 +3202,322 @@ void LockPassSlixIso15693(uint32_t pass_id, uint32_t password) {
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// Backdoor blocks of a magic "Gen1" tag, see doc/magic_cards_notes.md
|
||||
#define ISO15_MAGIC_GEN1_BLK_UID_LO 0x38
|
||||
#define ISO15_MAGIC_GEN1_BLK_UID_HI 0x39
|
||||
#define ISO15_MAGIC_GEN1_BLK_UNLOCK_A 0x3E
|
||||
#define ISO15_MAGIC_GEN1_BLK_UNLOCK_B 0x3F
|
||||
|
||||
static const uint8_t g_magic_gen1_blank[4] = {0x00, 0x00, 0x00, 0x00};
|
||||
static const uint8_t g_magic_gen1_unlock_b[4] = {0x69, 0x96, 0x00, 0x00};
|
||||
|
||||
// true when every byte is zero
|
||||
static bool magic_15_is_blank(const uint8_t *data, uint8_t len) {
|
||||
for (uint8_t i = 0; i < len; i++) {
|
||||
if (data[i] != 0x00) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// PM3_SUCCESS means the tag answered with block data, *data then points into
|
||||
// the receive buffer and stays valid until the next command overwrites it.
|
||||
static int magic_15_read_blk(uint8_t blockno, uint8_t **data, uint8_t *len, uint32_t *start_time, uint32_t *eof_time) {
|
||||
|
||||
uint8_t cmd[5] = {ISO15_REQ_DATARATE_HIGH | ISO15_REQ_OPTION, ISO15693_READBLOCK, blockno, 0x00, 0x00};
|
||||
AddCrc15(cmd, 3);
|
||||
|
||||
uint16_t recvlen = 0;
|
||||
int res = SendDataTag(cmd, sizeof(cmd), false, true, s_iso15_recvbuf, sizeof(s_iso15_recvbuf),
|
||||
*start_time, ISO15693_READER_TIMEOUT, eof_time, &recvlen);
|
||||
|
||||
*start_time = *eof_time + DELAY_ISO15693_VICC_TO_VCD_READER;
|
||||
|
||||
if (res != PM3_SUCCESS) {
|
||||
return res;
|
||||
}
|
||||
|
||||
// flags + at least four data bytes + crc
|
||||
if (recvlen < 7) {
|
||||
return PM3_EWRONGANSWER;
|
||||
}
|
||||
|
||||
// GetIso15693AnswerFromTag() doesn't check the crc, so do it here
|
||||
if (CheckCrc15(s_iso15_recvbuf, recvlen) == false) {
|
||||
return PM3_ECRC;
|
||||
}
|
||||
|
||||
if ((s_iso15_recvbuf[0] & ISO15_RES_ERROR) == ISO15_RES_ERROR) {
|
||||
return PM3_EWRONGANSWER;
|
||||
}
|
||||
|
||||
// the option flag makes the tag prepend a lock byte, blocks can be wider than four bytes
|
||||
uint8_t offset = (recvlen > 7) ? 2 : 1;
|
||||
*data = s_iso15_recvbuf + offset;
|
||||
*len = recvlen - offset - 2;
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
// Unaddressed block write, leaves the field on for the next command
|
||||
static int magic_15_write_blk(uint8_t blockno, const uint8_t *data, uint32_t *start_time, uint32_t *eof_time) {
|
||||
|
||||
uint8_t cmd[9] = {ISO15_REQ_DATARATE_HIGH, ISO15693_WRITEBLOCK, blockno, data[0], data[1], data[2], data[3], 0x00, 0x00};
|
||||
AddCrc15(cmd, 7);
|
||||
|
||||
uint16_t recvlen = 0;
|
||||
int res = SendDataTag(cmd, sizeof(cmd), false, true, s_iso15_recvbuf, sizeof(s_iso15_recvbuf),
|
||||
*start_time, ISO15693_READER_TIMEOUT_WRITE, eof_time, &recvlen);
|
||||
|
||||
*start_time = *eof_time + DELAY_ISO15693_VICC_TO_VCD_READER;
|
||||
|
||||
if (res != PM3_SUCCESS) {
|
||||
return res;
|
||||
}
|
||||
|
||||
// flags + crc
|
||||
if (recvlen < 3) {
|
||||
return PM3_EWRONGANSWER;
|
||||
}
|
||||
|
||||
if (CheckCrc15(s_iso15_recvbuf, recvlen) == false) {
|
||||
return PM3_ECRC;
|
||||
}
|
||||
|
||||
if ((s_iso15_recvbuf[0] & ISO15_RES_ERROR) == ISO15_RES_ERROR) {
|
||||
return PM3_EWRONGANSWER;
|
||||
}
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
// On a real Gen1 tag these four blocks sit outside the readable memory and the read fails.
|
||||
// A tag that answers with data is a normal tag and we must not overwrite it.
|
||||
static bool magic_gen1_is_backdoor(uint32_t *start_time, uint32_t *eof_time) {
|
||||
|
||||
static const uint8_t blocks[4] = {
|
||||
ISO15_MAGIC_GEN1_BLK_UID_LO,
|
||||
ISO15_MAGIC_GEN1_BLK_UID_HI,
|
||||
ISO15_MAGIC_GEN1_BLK_UNLOCK_A,
|
||||
ISO15_MAGIC_GEN1_BLK_UNLOCK_B
|
||||
};
|
||||
|
||||
for (uint8_t i = 0; i < ARRAYLEN(blocks); i++) {
|
||||
|
||||
uint8_t *data = NULL;
|
||||
uint8_t len = 0;
|
||||
|
||||
if (magic_15_read_blk(blocks[i], &data, &len, start_time, eof_time) != PM3_SUCCESS) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (magic_15_is_blank(data, len)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// leftover of the unlock write of an earlier attempt on this same tag
|
||||
if (blocks[i] == ISO15_MAGIC_GEN1_BLK_UNLOCK_B && len >= 4 &&
|
||||
memcmp(data, g_magic_gen1_unlock_b, 4) == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (g_dbglevel >= DBG_ERROR) {
|
||||
Dbprintf("SetTag15693Uid: block 0x%02X holds data, not a magic Gen1 tag", blocks[i]);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Blank the four blocks again so a failed attempt leaves no unlock values behind
|
||||
static void magic_gen1_rollback(uint32_t *start_time, uint32_t *eof_time) {
|
||||
magic_15_write_blk(ISO15_MAGIC_GEN1_BLK_UID_LO, g_magic_gen1_blank, start_time, eof_time);
|
||||
magic_15_write_blk(ISO15_MAGIC_GEN1_BLK_UID_HI, g_magic_gen1_blank, start_time, eof_time);
|
||||
magic_15_write_blk(ISO15_MAGIC_GEN1_BLK_UNLOCK_A, g_magic_gen1_blank, start_time, eof_time);
|
||||
magic_15_write_blk(ISO15_MAGIC_GEN1_BLK_UNLOCK_B, g_magic_gen1_blank, start_time, eof_time);
|
||||
}
|
||||
|
||||
// Set the UID on Magic ISO15693 tag (based on Iceman's LUA-script).
|
||||
void SetTag15693Uid(const uint8_t *uid) {
|
||||
|
||||
LED_A_ON();
|
||||
uint8_t cmd[4][9] = {
|
||||
{ISO15_REQ_DATARATE_HIGH, ISO15693_WRITEBLOCK, 0x3e, 0x00, 0x00, 0x00, 0x00, 0xE9, 0x8F},
|
||||
{ISO15_REQ_DATARATE_HIGH, ISO15693_WRITEBLOCK, 0x3f, 0x69, 0x96, 0x00, 0x00, 0x8A, 0xBB},
|
||||
|
||||
// Command 3 : 02 21 38 u8u7u6u5 (where uX = uid byte X)
|
||||
{ISO15_REQ_DATARATE_HIGH, ISO15693_WRITEBLOCK, 0x38, uid[7], uid[6], uid[5], uid[4]},
|
||||
|
||||
// Command 4 : 02 21 39 u4u3u2u1 (where uX = uid byte X)
|
||||
{ISO15_REQ_DATARATE_HIGH, ISO15693_WRITEBLOCK, 0x39, uid[3], uid[2], uid[1], uid[0]}
|
||||
};
|
||||
|
||||
|
||||
AddCrc15(cmd[2], 7);
|
||||
AddCrc15(cmd[3], 7);
|
||||
|
||||
memset(s_iso15_recvbuf, 0, sizeof(s_iso15_recvbuf));
|
||||
|
||||
uint32_t start_time = 0;
|
||||
uint32_t eof_time = 0;
|
||||
uint16_t recvlen = 0;
|
||||
Iso15693InitReader();
|
||||
StartCountSspClk();
|
||||
uint32_t start_time = 0, eof_time = 0;
|
||||
|
||||
int res = PM3_SUCCESS;
|
||||
uint8_t origuid[8] = {0};
|
||||
if (get_uid_slix(start_time, &eof_time, origuid) != PM3_SUCCESS) {
|
||||
reply_ng(CMD_HF_ISO15693_CSETUID, PM3_ETIMEOUT, NULL, 0);
|
||||
switch_off();
|
||||
return;
|
||||
}
|
||||
start_time = eof_time + DELAY_ISO15693_VICC_TO_VCD_READER;
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
res = SendDataTag(
|
||||
cmd[i],
|
||||
sizeof(cmd[i]),
|
||||
(i == 0) ? true : false,
|
||||
true,
|
||||
s_iso15_recvbuf,
|
||||
sizeof(s_iso15_recvbuf),
|
||||
start_time,
|
||||
ISO15693_READER_TIMEOUT_WRITE,
|
||||
&eof_time,
|
||||
&recvlen
|
||||
);
|
||||
|
||||
start_time = eof_time + DELAY_ISO15693_VICC_TO_VCD_READER;
|
||||
if (magic_gen1_is_backdoor(&start_time, &eof_time) == false) {
|
||||
reply_ng(CMD_HF_ISO15693_CSETUID, PM3_EWRONGANSWER, NULL, 0);
|
||||
switch_off();
|
||||
return;
|
||||
}
|
||||
|
||||
reply_ng(CMD_HF_ISO15693_CSETUID, res, NULL, 0);
|
||||
const uint8_t uid_lo[4] = {uid[7], uid[6], uid[5], uid[4]};
|
||||
const uint8_t uid_hi[4] = {uid[3], uid[2], uid[1], uid[0]};
|
||||
|
||||
// the unlock writes are best-effort, a tag already unlocked rejects them
|
||||
magic_15_write_blk(ISO15_MAGIC_GEN1_BLK_UNLOCK_A, g_magic_gen1_blank, &start_time, &eof_time);
|
||||
magic_15_write_blk(ISO15_MAGIC_GEN1_BLK_UNLOCK_B, g_magic_gen1_unlock_b, &start_time, &eof_time);
|
||||
|
||||
int res = magic_15_write_blk(ISO15_MAGIC_GEN1_BLK_UID_LO, uid_lo, &start_time, &eof_time);
|
||||
if (res == PM3_SUCCESS) {
|
||||
res = magic_15_write_blk(ISO15_MAGIC_GEN1_BLK_UID_HI, uid_hi, &start_time, &eof_time);
|
||||
}
|
||||
|
||||
// get_uid_slix answers LSB first, the requested uid is MSB first
|
||||
uint8_t newuid[8] = {0}, revuid[8] = {0};
|
||||
int res_uid = get_uid_slix(start_time, &eof_time, newuid);
|
||||
reverse_array_copy(newuid, sizeof(newuid), revuid);
|
||||
start_time = eof_time + DELAY_ISO15693_VICC_TO_VCD_READER;
|
||||
|
||||
if (res == PM3_SUCCESS && res_uid == PM3_SUCCESS && memcmp(revuid, uid, sizeof(revuid)) == 0) {
|
||||
reply_ng(CMD_HF_ISO15693_CSETUID, PM3_SUCCESS, NULL, 0);
|
||||
switch_off();
|
||||
return;
|
||||
}
|
||||
|
||||
// only roll back while the tag still shows its original UID, blanking the
|
||||
// blocks after a UID change would destroy what we just wrote
|
||||
if (res_uid == PM3_SUCCESS && memcmp(newuid, origuid, sizeof(newuid)) == 0) {
|
||||
magic_gen1_rollback(&start_time, &eof_time);
|
||||
}
|
||||
|
||||
reply_ng(CMD_HF_ISO15693_CSETUID, PM3_ESOFT, NULL, 0);
|
||||
switch_off();
|
||||
}
|
||||
|
||||
// Magic "V3" tags answer with a signature in 0x14/0x15 while the UID is still changeable
|
||||
#define ISO15_MAGIC_V3_BLK_UID_LO 0x10
|
||||
#define ISO15_MAGIC_V3_BLK_UID_HI 0x11
|
||||
#define ISO15_MAGIC_V3_BLK_SIG_A 0x14
|
||||
#define ISO15_MAGIC_V3_BLK_SIG_B 0x15
|
||||
|
||||
static const uint8_t g_magic_v3_sig_a[4] = {0xA5, 0x2B, 0x44, 0x2C};
|
||||
static const uint8_t g_magic_v3_sig_b[4] = {0x21, 0xAE, 0x93, 0x00};
|
||||
static const uint8_t g_magic_v3_fin_a[4] = {0xA5, 0x2B, 0x44, 0x2C};
|
||||
static const uint8_t g_magic_v3_fin_b[4] = {0x69, 0xE2, 0x5D, 0x00};
|
||||
|
||||
// Writing the finalize values to anything that is not an un-finalized V3 tag bricks it.
|
||||
// PM3_SUCCESS means both signature blocks were read, *in_config says what they hold.
|
||||
static int magic_v3_read_config(bool *in_config, uint32_t *start_time, uint32_t *eof_time) {
|
||||
|
||||
uint8_t *data = NULL;
|
||||
uint8_t len = 0;
|
||||
|
||||
*in_config = false;
|
||||
|
||||
// the second read overwrites the buffer, so compare 0x14 before asking for 0x15
|
||||
int res = magic_15_read_blk(ISO15_MAGIC_V3_BLK_SIG_A, &data, &len, start_time, eof_time);
|
||||
if (res != PM3_SUCCESS) {
|
||||
return res;
|
||||
}
|
||||
|
||||
bool sig_a = (len >= 4 && memcmp(data, g_magic_v3_sig_a, 4) == 0);
|
||||
|
||||
res = magic_15_read_blk(ISO15_MAGIC_V3_BLK_SIG_B, &data, &len, start_time, eof_time);
|
||||
if (res != PM3_SUCCESS) {
|
||||
return res;
|
||||
}
|
||||
|
||||
*in_config = (sig_a && len >= 4 && memcmp(data, g_magic_v3_sig_b, 4) == 0);
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
// Set the UID on a magic "V3" tag, repeatable until the tag is finalized
|
||||
void SetTag15693Uid_v3(const uint8_t *uid) {
|
||||
|
||||
LED_A_ON();
|
||||
memset(s_iso15_recvbuf, 0, sizeof(s_iso15_recvbuf));
|
||||
|
||||
Iso15693InitReader();
|
||||
StartCountSspClk();
|
||||
uint32_t start_time = 0, eof_time = 0;
|
||||
|
||||
uint8_t curuid[8] = {0};
|
||||
if (get_uid_slix(start_time, &eof_time, curuid) != PM3_SUCCESS) {
|
||||
reply_ng(CMD_HF_ISO15693_CSETUID_V3, PM3_ETIMEOUT, NULL, 0);
|
||||
switch_off();
|
||||
return;
|
||||
}
|
||||
start_time = eof_time + DELAY_ISO15693_VICC_TO_VCD_READER;
|
||||
|
||||
bool in_config = false;
|
||||
if (magic_v3_read_config(&in_config, &start_time, &eof_time) != PM3_SUCCESS || in_config == false) {
|
||||
reply_ng(CMD_HF_ISO15693_CSETUID_V3, PM3_EWRONGANSWER, NULL, 0);
|
||||
switch_off();
|
||||
return;
|
||||
}
|
||||
|
||||
const uint8_t uid_lo[4] = {uid[7], uid[6], uid[5], uid[4]};
|
||||
const uint8_t uid_hi[4] = {uid[3], uid[2], uid[1], uid[0]};
|
||||
|
||||
int res = magic_15_write_blk(ISO15_MAGIC_V3_BLK_UID_LO, uid_lo, &start_time, &eof_time);
|
||||
if (res == PM3_SUCCESS) {
|
||||
res = magic_15_write_blk(ISO15_MAGIC_V3_BLK_UID_HI, uid_hi, &start_time, &eof_time);
|
||||
}
|
||||
|
||||
if (res == PM3_SUCCESS) {
|
||||
// get_uid_slix answers LSB first, the requested uid is MSB first
|
||||
uint8_t newuid[8] = {0}, revuid[8] = {0};
|
||||
res = get_uid_slix(start_time, &eof_time, newuid);
|
||||
reverse_array_copy(newuid, sizeof(newuid), revuid);
|
||||
|
||||
if (res == PM3_SUCCESS && memcmp(revuid, uid, sizeof(revuid)) != 0) {
|
||||
res = PM3_ESOFT;
|
||||
}
|
||||
}
|
||||
|
||||
reply_ng(CMD_HF_ISO15693_CSETUID_V3, (res == PM3_SUCCESS) ? PM3_SUCCESS : PM3_ESOFT, NULL, 0);
|
||||
switch_off();
|
||||
}
|
||||
|
||||
// Lock the UID of a magic "V3" tag, irreversible
|
||||
void FinalizeTag15693_v3(void) {
|
||||
|
||||
LED_A_ON();
|
||||
memset(s_iso15_recvbuf, 0, sizeof(s_iso15_recvbuf));
|
||||
|
||||
Iso15693InitReader();
|
||||
StartCountSspClk();
|
||||
uint32_t start_time = 0, eof_time = 0;
|
||||
|
||||
uint8_t curuid[8] = {0};
|
||||
if (get_uid_slix(start_time, &eof_time, curuid) != PM3_SUCCESS) {
|
||||
reply_ng(CMD_HF_ISO15693_CFINALIZE_V3, PM3_ETIMEOUT, NULL, 0);
|
||||
switch_off();
|
||||
return;
|
||||
}
|
||||
start_time = eof_time + DELAY_ISO15693_VICC_TO_VCD_READER;
|
||||
|
||||
bool in_config = false;
|
||||
if (magic_v3_read_config(&in_config, &start_time, &eof_time) != PM3_SUCCESS || in_config == false) {
|
||||
reply_ng(CMD_HF_ISO15693_CFINALIZE_V3, PM3_EWRONGANSWER, NULL, 0);
|
||||
switch_off();
|
||||
return;
|
||||
}
|
||||
|
||||
int res = magic_15_write_blk(ISO15_MAGIC_V3_BLK_SIG_A, g_magic_v3_fin_a, &start_time, &eof_time);
|
||||
if (res == PM3_SUCCESS) {
|
||||
res = magic_15_write_blk(ISO15_MAGIC_V3_BLK_SIG_B, g_magic_v3_fin_b, &start_time, &eof_time);
|
||||
}
|
||||
|
||||
// finalize wipes the memory, so the signature blocks must read back as something
|
||||
// else. a read that fails proves nothing and counts as a failure too
|
||||
if (res == PM3_SUCCESS) {
|
||||
if (magic_v3_read_config(&in_config, &start_time, &eof_time) != PM3_SUCCESS || in_config) {
|
||||
res = PM3_ESOFT;
|
||||
}
|
||||
}
|
||||
|
||||
reply_ng(CMD_HF_ISO15693_CFINALIZE_V3, (res == PM3_SUCCESS) ? PM3_SUCCESS : PM3_ESOFT, NULL, 0);
|
||||
switch_off();
|
||||
}
|
||||
|
||||
|
||||
@@ -59,6 +59,8 @@ int SendDataTagEOF(uint8_t *recv, uint16_t max_recv_len, uint32_t start_time, ui
|
||||
|
||||
void SetTag15693Uid(const uint8_t *uid);
|
||||
void SetTag15693Uid_v2(const uint8_t *uid);
|
||||
void SetTag15693Uid_v3(const uint8_t *uid);
|
||||
void FinalizeTag15693_v3(void);
|
||||
|
||||
void WritePasswordSlixIso15693(const uint8_t *old_password, const uint8_t *new_password, uint8_t pwd_id);
|
||||
void DisablePrivacySlixIso15693(const uint8_t *password);
|
||||
|
||||
+69
-126
@@ -3324,79 +3324,6 @@ static int CmdHF15Restore(const char *Cmd) {
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
// ISO15693 magic "V3" tag support
|
||||
#define ISO15_MAGIC_V3_BLK_UID_LO 0x10
|
||||
#define ISO15_MAGIC_V3_BLK_UID_HI 0x11
|
||||
#define ISO15_MAGIC_V3_BLK_SIG_A 0x14
|
||||
#define ISO15_MAGIC_V3_BLK_SIG_B 0x15
|
||||
|
||||
// signature check in block 0x14/0x15
|
||||
static const uint8_t iso15_magic_v3_sig_a[4] = {0xA5, 0x2B, 0x44, 0x2C};
|
||||
static const uint8_t iso15_magic_v3_sig_b[4] = {0x21, 0xAE, 0x93, 0x00};
|
||||
|
||||
// finalize command write in block 0x14/0x15
|
||||
// hf 15 raw -wac -d 022114A52B442C
|
||||
// hf 15 raw -wac -d 02211569E25D00
|
||||
static const uint8_t iso15_magic_v3_fin_a[4] = {0xA5, 0x2B, 0x44, 0x2C};
|
||||
static const uint8_t iso15_magic_v3_fin_b[4] = {0x69, 0xE2, 0x5D, 0x00};
|
||||
|
||||
// Read one 4-byte block from a magic V3 tag in unaddressed mode.
|
||||
static int hf15_magic_v3_read_blk(uint8_t blockno, uint8_t out[4]) {
|
||||
|
||||
uint16_t approxlen = 2 + 1 + 2;
|
||||
iso15_raw_cmd_t *packet = (iso15_raw_cmd_t *)calloc(1, sizeof(iso15_raw_cmd_t) + approxlen);
|
||||
if (packet == NULL) {
|
||||
PrintAndLogEx(WARNING, "Failed to allocate memory");
|
||||
return PM3_EMALLOC;
|
||||
}
|
||||
|
||||
// enforce OPTION flag so we get the lock byte, keeping data at offset 2
|
||||
packet->raw[packet->rawlen++] = arg_get_raw_flag(0, true, false, true);
|
||||
packet->raw[packet->rawlen++] = ISO15693_READBLOCK;
|
||||
packet->raw[packet->rawlen++] = blockno;
|
||||
AddCrc15(packet->raw, packet->rawlen);
|
||||
packet->rawlen += 2;
|
||||
packet->flags = (ISO15_CONNECT | ISO15_READ_RESPONSE);
|
||||
|
||||
clearCommandBuffer();
|
||||
SendCommandNG(CMD_HF_ISO15693_COMMAND, (uint8_t *)packet, ISO15_RAW_LEN(packet->rawlen));
|
||||
free(packet);
|
||||
|
||||
PacketResponseNG resp;
|
||||
if (WaitForResponseTimeout(CMD_HF_ISO15693_COMMAND, &resp, 2000) == false) {
|
||||
PrintAndLogEx(DEBUG, "iso15693 timeout");
|
||||
return PM3_ETIMEOUT;
|
||||
}
|
||||
|
||||
ISO15_ERROR_HANDLING_RESPONSE
|
||||
|
||||
uint8_t *d = resp.data.asBytes;
|
||||
|
||||
ISO15_ERROR_HANDLING_CARD_RESPONSE(d, resp.length)
|
||||
|
||||
memcpy(out, d + 2, 4);
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
// Write one 4-byte block to a magic V3 tag in unaddressed mode.
|
||||
static int hf15_magic_v3_write_blk(uint8_t blockno, const uint8_t *data) {
|
||||
uint16_t flags = arg_get_raw_flag(0, true, false, false);
|
||||
return hf_15_write_blk(NULL, flags, NULL, true, blockno, data, 4);
|
||||
}
|
||||
|
||||
// Detect an un-finalized magic V3 tag by its configuration-mode signature.
|
||||
static bool hf15_magic_v3_is_config_mode(void) {
|
||||
uint8_t a[4] = {0};
|
||||
uint8_t b[4] = {0};
|
||||
if (hf15_magic_v3_read_blk(ISO15_MAGIC_V3_BLK_SIG_A, a) != PM3_SUCCESS) {
|
||||
return false;
|
||||
}
|
||||
if (hf15_magic_v3_read_blk(ISO15_MAGIC_V3_BLK_SIG_B, b) != PM3_SUCCESS) {
|
||||
return false;
|
||||
}
|
||||
return (memcmp(a, iso15_magic_v3_sig_a, 4) == 0) && (memcmp(b, iso15_magic_v3_sig_b, 4) == 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Commandline handling: HF15 CMD CSETUID
|
||||
* Set UID for magic Chinese card
|
||||
@@ -3406,6 +3333,8 @@ static int CmdHF15CSetUID(const char *Cmd) {
|
||||
CLIParserContext *ctx;
|
||||
CLIParserInit(&ctx, "hf 15 csetuid",
|
||||
"Set UID for magic Chinese card (only works with such cards)\n"
|
||||
"Gen1 and V3 probe their backdoor blocks first and refuse to write if they\n"
|
||||
"read back real data, to avoid corrupting a normal (non-magic) tag.\n"
|
||||
"For magic 'V3' tags this writes the UID configuration only and is repeatable;\n"
|
||||
"run `" _YELLOW_("hf 15 cfinalize") "` afterwards to lock the UID permanently.",
|
||||
"hf 15 csetuid -u E011223344556677 -> use gen1 command\n"
|
||||
@@ -3459,38 +3388,50 @@ static int CmdHF15CSetUID(const char *Cmd) {
|
||||
|
||||
PrintAndLogEx(INFO, "Writing...");
|
||||
|
||||
if (use_v3) {
|
||||
/*
|
||||
for example id: E011223344556677
|
||||
[=] 16 | 77 66 55 44 | 0 | wfUD
|
||||
[=] 17 | 33 22 11 E0 | 0 | 3"..
|
||||
*/
|
||||
uint8_t blk_lo[4] = {0};
|
||||
uint8_t blk_hi[4] = {0};
|
||||
reverse_array_copy(payload.uid + 4, 4, blk_lo);
|
||||
reverse_array_copy(payload.uid, 4, blk_hi);
|
||||
PacketResponseNG resp;
|
||||
clearCommandBuffer();
|
||||
|
||||
if (hf15_magic_v3_write_blk(ISO15_MAGIC_V3_BLK_UID_LO, blk_lo) != PM3_SUCCESS ||
|
||||
hf15_magic_v3_write_blk(ISO15_MAGIC_V3_BLK_UID_HI, blk_hi) != PM3_SUCCESS) {
|
||||
PrintAndLogEx(FAILED, "Setting new UID ( " _RED_("fail") " )");
|
||||
PrintAndLogEx(NORMAL, "");
|
||||
return PM3_ESOFT;
|
||||
}
|
||||
uint16_t cmd = CMD_HF_ISO15693_CSETUID;
|
||||
if (use_v2) {
|
||||
cmd = CMD_HF_ISO15693_CSETUID_V2;
|
||||
} else if (use_v3) {
|
||||
cmd = CMD_HF_ISO15693_CSETUID_V3;
|
||||
}
|
||||
|
||||
} else {
|
||||
PacketResponseNG resp;
|
||||
clearCommandBuffer();
|
||||
SendCommandNG(cmd, (uint8_t *)&payload, sizeof(payload));
|
||||
if (WaitForResponseTimeout(cmd, &resp, 2000) == false) {
|
||||
PrintAndLogEx(WARNING, "timeout while waiting for reply");
|
||||
DropField();
|
||||
return PM3_ESOFT;
|
||||
}
|
||||
|
||||
uint16_t cmd = CMD_HF_ISO15693_CSETUID;
|
||||
if (use_v2) {
|
||||
cmd = CMD_HF_ISO15693_CSETUID_V2;
|
||||
}
|
||||
// Gen1 and V3 identify the tag and verify the new UID on the device, V2 has neither
|
||||
if (use_v2 == false) {
|
||||
|
||||
SendCommandNG(cmd, (uint8_t *)&payload, sizeof(payload));
|
||||
if (WaitForResponseTimeout(cmd, &resp, 2000) == false) {
|
||||
PrintAndLogEx(WARNING, "timeout while waiting for reply");
|
||||
DropField();
|
||||
return PM3_ESOFT;
|
||||
switch (resp.status) {
|
||||
case PM3_SUCCESS:
|
||||
break;
|
||||
|
||||
case PM3_ETIMEOUT:
|
||||
PrintAndLogEx(FAILED, "no tag found");
|
||||
return PM3_ESOFT;
|
||||
|
||||
case PM3_EWRONGANSWER:
|
||||
if (use_v3) {
|
||||
PrintAndLogEx(FAILED, "tag is not an un-finalized magic " _YELLOW_("V3") " tag");
|
||||
PrintAndLogEx(HINT, "Hint: signature in blocks 0x14/0x15 not found");
|
||||
PrintAndLogEx(HINT, "already finalized or not a V3 tag");
|
||||
} else {
|
||||
PrintAndLogEx(FAILED, "tag doesn't look like a blank/magic " _YELLOW_("Gen1") " tag ( " _RED_("fail") " )");
|
||||
PrintAndLogEx(HINT, "Hint: block 0x38/0x39/0x3E/0x3F must be unreadable or blank");
|
||||
PrintAndLogEx(HINT, "refusing to risk overwriting a normal tag");
|
||||
}
|
||||
return PM3_ESOFT;
|
||||
|
||||
default:
|
||||
PrintAndLogEx(FAILED, "Setting new UID ( " _RED_("fail") " )");
|
||||
PrintAndLogEx(NORMAL, "");
|
||||
return PM3_ESOFT;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3525,10 +3466,10 @@ static int CmdHF15CFinalize(const char *Cmd) {
|
||||
CLIParserContext *ctx;
|
||||
CLIParserInit(&ctx, "hf 15 cfinalize",
|
||||
"Finalize a magic ISO15693 'V3' tag.\n"
|
||||
_RED_("This operation is irreversible.") " After finalize the configuration\n"
|
||||
"area is erased and the UID can no longer be changed. Set the UID with\n"
|
||||
"`" _YELLOW_("hf 15 csetuid --v3") "` first, then lock it in with this command.",
|
||||
"hf 15 cfinalize -y"
|
||||
_RED_("This operation is irreversible.") " It locks the UID for good,\n"
|
||||
"clears the configuration blocks and shifts the block numbering. Set it with\n"
|
||||
"`" _YELLOW_("hf 15 csetuid --v3") "` first, then lock it in with this command.",
|
||||
"hf 15 cfinalize -y"
|
||||
);
|
||||
|
||||
void *argtable[] = {
|
||||
@@ -3540,34 +3481,36 @@ static int CmdHF15CFinalize(const char *Cmd) {
|
||||
bool confirmed = arg_get_lit(ctx, 1);
|
||||
CLIParserFree(ctx);
|
||||
|
||||
PrintAndLogEx(INFO, "Get current tag");
|
||||
|
||||
uint8_t carduid[ISO15693_UID_LENGTH] = {0x00};
|
||||
if (getUID(true, false, carduid) != PM3_SUCCESS) {
|
||||
PrintAndLogEx(FAILED, "no tag found");
|
||||
return PM3_ESOFT;
|
||||
}
|
||||
|
||||
// Safety: only proceed on a tag that is actually an un-finalized magic V3.
|
||||
// Writing the finalize values to any other tag may permanently brick it.
|
||||
if (hf15_magic_v3_is_config_mode() == false) {
|
||||
PrintAndLogEx(FAILED, "tag is not an un-finalized magic " _YELLOW_("V3") " tag");
|
||||
PrintAndLogEx(HINT, "Hint: signature in blocks 0x14/0x15 not found - already finalized or not a V3 tag");
|
||||
return PM3_ESOFT;
|
||||
}
|
||||
|
||||
PrintAndLogEx(SUCCESS, "Magic " _GREEN_("V3") " tag in configuration mode ( " _GREEN_("ok") " )");
|
||||
|
||||
if (confirmed == false) {
|
||||
PrintAndLogEx(WARNING, _RED_("This operation is irreversible!") " The UID will be locked permanently.");
|
||||
PrintAndLogEx(WARNING, _RED_("This operation is irreversible!") " The UID is locked and the blocks move.");
|
||||
PrintAndLogEx(WARNING, "Add " _YELLOW_("-y") " to confirm and proceed.");
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
|
||||
PrintAndLogEx(INFO, "Finalizing...");
|
||||
|
||||
if (hf15_magic_v3_write_blk(ISO15_MAGIC_V3_BLK_SIG_A, iso15_magic_v3_fin_a) != PM3_SUCCESS ||
|
||||
hf15_magic_v3_write_blk(ISO15_MAGIC_V3_BLK_SIG_B, iso15_magic_v3_fin_b) != PM3_SUCCESS) {
|
||||
PacketResponseNG resp;
|
||||
clearCommandBuffer();
|
||||
SendCommandNG(CMD_HF_ISO15693_CFINALIZE_V3, NULL, 0);
|
||||
if (WaitForResponseTimeout(CMD_HF_ISO15693_CFINALIZE_V3, &resp, 2000) == false) {
|
||||
PrintAndLogEx(WARNING, "timeout while waiting for reply");
|
||||
DropField();
|
||||
return PM3_ESOFT;
|
||||
}
|
||||
|
||||
if (resp.status == PM3_ETIMEOUT) {
|
||||
PrintAndLogEx(FAILED, "no tag found");
|
||||
return PM3_ESOFT;
|
||||
}
|
||||
|
||||
if (resp.status == PM3_EWRONGANSWER) {
|
||||
PrintAndLogEx(FAILED, "tag is not an un-finalized magic " _YELLOW_("V3") " tag");
|
||||
PrintAndLogEx(HINT, "Hint: signature in blocks 0x14/0x15 not found");
|
||||
PrintAndLogEx(HINT, "already finalized or not a V3 tag");
|
||||
return PM3_ESOFT;
|
||||
}
|
||||
|
||||
if (resp.status != PM3_SUCCESS) {
|
||||
PrintAndLogEx(FAILED, "Finalize ( " _RED_("fail") " )");
|
||||
PrintAndLogEx(NORMAL, "");
|
||||
return PM3_ESOFT;
|
||||
|
||||
+35
-6
@@ -1913,7 +1913,7 @@
|
||||
},
|
||||
"hf 15 cfinalize": {
|
||||
"command": "hf 15 cfinalize",
|
||||
"description": "Finalize a magic ISO15693 'V3' tag. This operation is irreversible. After finalize the configuration area is erased and the UID can no longer be changed. Set the UID with `hf 15 csetuid --v3` first, then lock it in with this command.",
|
||||
"description": "Finalize a magic ISO15693 'V3' tag. This operation is irreversible. It locks the UID for good, clears the configuration blocks and shifts the block numbering. Set it with `hf 15 csetuid --v3` first, then lock it in with this command.",
|
||||
"notes": [
|
||||
"hf 15 cfinalize -y"
|
||||
],
|
||||
@@ -1926,7 +1926,7 @@
|
||||
},
|
||||
"hf 15 csetuid": {
|
||||
"command": "hf 15 csetuid",
|
||||
"description": "Set UID for magic Chinese card (only works with such cards) For magic 'V3' tags this writes the UID configuration only and is repeatable; run `hf 15 cfinalize` afterwards to lock the UID permanently.",
|
||||
"description": "Set UID for magic Chinese card (only works with such cards) Gen1 and V3 probe their backdoor blocks first and refuse to write if they read back real data, to avoid corrupting a normal (non-magic) tag. For magic 'V3' tags this writes the UID configuration only and is repeatable; run `hf 15 cfinalize` afterwards to lock the UID permanently.",
|
||||
"notes": [
|
||||
"hf 15 csetuid -u E011223344556677 -> use gen1 command",
|
||||
"hf 15 csetuid -u E011223344556677 --v2 -> use gen2 command",
|
||||
@@ -9853,6 +9853,20 @@
|
||||
],
|
||||
"usage": "hf ntag424 getfs [-h] --fileno <dec>"
|
||||
},
|
||||
"hf ntag424 gettt": {
|
||||
"command": "hf ntag424 gettt",
|
||||
"description": "Read and print Tag Tamper status. Will authenticate if key information is provided.",
|
||||
"notes": [
|
||||
"hf ntag424 gettt --keyno 0 -k 00000000000000000000000000000000"
|
||||
],
|
||||
"offline": false,
|
||||
"options": [
|
||||
"-h, --help This help",
|
||||
"--keyno <dec> Key number",
|
||||
"-k, --key <hex> Key for authentication (HEX 16 bytes)"
|
||||
],
|
||||
"usage": "hf ntag424 gettt [-h] [--keyno <dec>] [-k <hex>]"
|
||||
},
|
||||
"hf ntag424 help": {
|
||||
"command": "hf ntag424 help",
|
||||
"description": "help This help view Display content from tag dump file --------------------------------------------------------------------------------------- hf ntag424 info available offline: no Get info about NXP NTAG424 DNA Family styled tag.",
|
||||
@@ -9885,6 +9899,21 @@
|
||||
],
|
||||
"usage": "hf ntag424 read [-h] --fileno <1|2|3> [--keyno <dec>] [-k <hex>] [-o <dec>] -l <dec> [-m <plain|mac|encrypt>]"
|
||||
},
|
||||
"hf ntag424 setconfig": {
|
||||
"command": "hf ntag424 setconfig",
|
||||
"description": "Change a PICC configuration option. Several options are one-way, see the datasheet before using this.",
|
||||
"notes": [
|
||||
"hf ntag424 setconfig -k 00000000000000000000000000000000 --option 07 -d 0100"
|
||||
],
|
||||
"offline": false,
|
||||
"options": [
|
||||
"-h, --help This help",
|
||||
"-k, --key0 <hex> Authentication key (must be key 0, HEX 16 bytes)",
|
||||
"--option <hex> Configuration option (HEX 1 byte)",
|
||||
"-d, --data <hex> Option data (HEX, 1..47 bytes)"
|
||||
],
|
||||
"usage": "hf ntag424 setconfig [-h] -k <hex> --option <hex> -d <hex> [-d <hex>]..."
|
||||
},
|
||||
"hf ntag424 view": {
|
||||
"command": "hf ntag424 view",
|
||||
"description": "Print a NTAG 424 DNA dump file (bin/eml/json)",
|
||||
@@ -12776,7 +12805,7 @@
|
||||
"-1, --ht1 Card type Hitag 1",
|
||||
"-2, --ht2 Card type Hitag 2",
|
||||
"-s, --hts Card type Hitag S",
|
||||
"-m, --htm Card type Hitag \u03bc"
|
||||
"-m, --htm Card type Hitag \u00b5"
|
||||
],
|
||||
"usage": "lf hitag eload [-h12sm] -f <fn>"
|
||||
},
|
||||
@@ -12794,7 +12823,7 @@
|
||||
"-1, --ht1 Card type Hitag 1",
|
||||
"-2, --ht2 Card type Hitag 2 (default)",
|
||||
"-s, --hts Card type Hitag S",
|
||||
"-m, --htm Card type Hitag \u03bc"
|
||||
"-m, --htm Card type Hitag \u00b5"
|
||||
],
|
||||
"usage": "lf hitag eview [-hv12sm]"
|
||||
},
|
||||
@@ -16616,8 +16645,8 @@
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
"commands_extracted": 924,
|
||||
"commands_extracted": 926,
|
||||
"extracted_by": "PM3Help2JSON v1.00",
|
||||
"extracted_on": "2026-09-16T22:14:12+00:00"
|
||||
"extracted_on": "2026-09-22T14:39:12+00:00"
|
||||
}
|
||||
}
|
||||
@@ -855,6 +855,8 @@ Check column "offline" for their availability.
|
||||
|`hf ntag424 getfs `|N |`Get file settings`
|
||||
|`hf ntag424 changefs `|N |`Change file settings`
|
||||
|`hf ntag424 changekey `|N |`Change key`
|
||||
|`hf ntag424 gettt `|N |`Get Tag Tamper status`
|
||||
|`hf ntag424 setconfig `|N |`Set PICC configuration option`
|
||||
|
||||
|
||||
### hf saflok
|
||||
|
||||
@@ -2716,7 +2716,7 @@ V3: blocks `0x14` and `0x15` hold a signature while the tag is not finalized.
|
||||
|
||||
```
|
||||
hf 15 raw -ac -d 022014 # A5 2B 44 2C
|
||||
hf 15 raw -ac -d 022015 # 21 AE 93 00 (69 E2 5D 00 after finalize)
|
||||
hf 15 raw -ac -d 022015 # 21 AE 93 00 (00 00 00 00 after finalize)
|
||||
```
|
||||
|
||||
|
||||
@@ -2765,7 +2765,10 @@ hf 15 raw -acw -d 02211077665544
|
||||
hf 15 raw -acw -d 022111332211E0
|
||||
```
|
||||
|
||||
Finalize locks the UID for good and can't be undone, block `0x15` changes to `69 E2 5D 00`:
|
||||
Finalize locks the UID for good and can't be undone. It clears the configuration
|
||||
blocks and changes the block numbering: on the tag tested, data written to block
|
||||
`0x38` before finalize read back at block `0x20` afterwards, a shift of `0x18`.
|
||||
User data is moved, not erased. Note down what is where before finalizing:
|
||||
|
||||
```
|
||||
hf 15 raw -acw -d 022114A52B442C
|
||||
|
||||
@@ -1034,6 +1034,8 @@ typedef struct {
|
||||
|
||||
#define CMD_HF_ISO15693_CSETUID 0x0316
|
||||
#define CMD_HF_ISO15693_CSETUID_V2 0x0333
|
||||
#define CMD_HF_ISO15693_CSETUID_V3 0x0334
|
||||
#define CMD_HF_ISO15693_CFINALIZE_V3 0x0335
|
||||
|
||||
#define CMD_LF_SNIFF_RAW_ADC 0x0360
|
||||
|
||||
|
||||
Reference in new issue
Block a user