mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2025-08-20 21:33:47 -07:00
refactored topaz commands. It still uses a global var with dynamic memory. Added rdbl, wrbl, view, dump commands to match rest of client
This commit is contained in:
parent
5a4b8c8224
commit
62980b5038
7 changed files with 567 additions and 139 deletions
|
@ -3,12 +3,14 @@ All notable changes to this project will be documented in this file.
|
||||||
This project uses the changelog in accordance with [keepchangelog](http://keepachangelog.com/). Please use this to write notable changes, which is not the same as git commit log...
|
This project uses the changelog in accordance with [keepchangelog](http://keepachangelog.com/). Please use this to write notable changes, which is not the same as git commit log...
|
||||||
|
|
||||||
## [unreleased][unreleased]
|
## [unreleased][unreleased]
|
||||||
|
- Added json topaz file format (@iceman1001)
|
||||||
|
- Added `hf topaz rdbl, wrbl, view` commands (@iceman1001)
|
||||||
- Added more details to the annotations of `hf mfdes list` output (@nvx)
|
- Added more details to the annotations of `hf mfdes list` output (@nvx)
|
||||||
- Changed `hf iclass view` and related to suppress consecutive blocks with repeated contents with a `-z` flag, or `prefs set output --dense` (@nvx)
|
- Changed `hf iclass view` and related to suppress consecutive blocks with repeated contents with a `-z` flag, or `prefs set output --dense` (@nvx)
|
||||||
- Changed `hf iclass list` to display matched keys on the CHECK command rather than the card response, and made it check for elite keys too (@nvx)
|
- Changed `hf iclass list` to display matched keys on the CHECK command rather than the card response, and made it check for elite keys too (@nvx)
|
||||||
- Fixed `hf iclass info` and `hf iclass view` key access info looking at the wrong card config bit (@nvx)
|
- Fixed `hf iclass info` and `hf iclass view` key access info looking at the wrong card config bit (@nvx)
|
||||||
- Added `hf gallagher decode` command and fix Gallagher diversification for card master key (@nvx)
|
- Added `hf gallagher decode` command and fix Gallagher diversification for card master key (@nvx)
|
||||||
- Added mmbit-002 (kibi-002, kb5004xk1) russian tag to `hf texkom read` command (@merlokk)
|
- Changed `hf texkom reader` - now supports mmbit-002 (kibi-002, kb5004xk1) russian tag (@merlokk)
|
||||||
- Added `hf sniff --smode` skip/group adc data to consume less memory. Now it can sniff very long signals (@merlokk)
|
- Added `hf sniff --smode` skip/group adc data to consume less memory. Now it can sniff very long signals (@merlokk)
|
||||||
- Added `hf fudan` skeleton commands (@iceman1001)
|
- Added `hf fudan` skeleton commands (@iceman1001)
|
||||||
- Added `--reboot-to-bootloader` arg to pm3
|
- Added `--reboot-to-bootloader` arg to pm3
|
||||||
|
|
|
@ -606,14 +606,13 @@ static int ndef_print_CC(uint8_t *data) {
|
||||||
uint8_t mlrule = (data[3] & 0x06) >> 1;
|
uint8_t mlrule = (data[3] & 0x06) >> 1;
|
||||||
uint8_t mbread = (data[3] & 0x01);
|
uint8_t mbread = (data[3] & 0x01);
|
||||||
|
|
||||||
PrintAndLogEx(SUCCESS, " Additional feature information");
|
PrintAndLogEx(SUCCESS, " %02X: Additional feature information", data[3]);
|
||||||
PrintAndLogEx(SUCCESS, " %02X", data[3]);
|
|
||||||
PrintAndLogEx(SUCCESS, " 00000000");
|
PrintAndLogEx(SUCCESS, " 00000000");
|
||||||
PrintAndLogEx(SUCCESS, " xxx - %02X: RFU ( %s )", msb3, (msb3 == 0) ? _GREEN_("ok") : _RED_("fail"));
|
PrintAndLogEx(SUCCESS, " xxx..... - %02X: RFU ( %s )", msb3, (msb3 == 0) ? _GREEN_("ok") : _RED_("fail"));
|
||||||
PrintAndLogEx(SUCCESS, " x - %02X: %s special frame", sf, (sf) ? "support" : "don\'t support");
|
PrintAndLogEx(SUCCESS, " ...x.... - %02X: %s special frame", sf, (sf) ? "support" : "don\'t support");
|
||||||
PrintAndLogEx(SUCCESS, " x - %02X: %s lock block", lb, (lb) ? "support" : "don\'t support");
|
PrintAndLogEx(SUCCESS, " ....x... - %02X: %s lock block", lb, (lb) ? "support" : "don\'t support");
|
||||||
PrintAndLogEx(SUCCESS, " xx - %02X: RFU ( %s )", mlrule, (mlrule == 0) ? _GREEN_("ok") : _RED_("fail"));
|
PrintAndLogEx(SUCCESS, " .....xx. - %02X: RFU ( %s )", mlrule, (mlrule == 0) ? _GREEN_("ok") : _RED_("fail"));
|
||||||
PrintAndLogEx(SUCCESS, " x - %02X: IC %s multiple block reads", mbread, (mbread) ? "support" : "don\'t support");
|
PrintAndLogEx(SUCCESS, " .......x - %02X: IC %s multiple block reads", mbread, (mbread) ? "support" : "don\'t support");
|
||||||
return PM3_SUCCESS;
|
return PM3_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3243,7 +3242,8 @@ static int CmdHF14AMfUPwdGen(const char *Cmd) {
|
||||||
}
|
}
|
||||||
|
|
||||||
PrintAndLogEx(INFO, "------------------.---------------");
|
PrintAndLogEx(INFO, "------------------.---------------");
|
||||||
PrintAndLogEx(INFO, " Using UID : " _YELLOW_("%s"), sprint_hex(uid, 7));
|
PrintAndLogEx(INFO, " Using UID 4b: " _YELLOW_("%s"), sprint_hex(uid, 4));
|
||||||
|
PrintAndLogEx(INFO, " Using UID 7b: " _YELLOW_("%s"), sprint_hex(uid, 7));
|
||||||
PrintAndLogEx(INFO, "----------------------------------");
|
PrintAndLogEx(INFO, "----------------------------------");
|
||||||
PrintAndLogEx(INFO, " algo | pwd | pack");
|
PrintAndLogEx(INFO, " algo | pwd | pack");
|
||||||
PrintAndLogEx(INFO, "-----------------+----------+-----");
|
PrintAndLogEx(INFO, "-----------------+----------+-----");
|
||||||
|
|
|
@ -38,25 +38,7 @@
|
||||||
# define AddCrc14B(data, len) compute_crc(CRC_14443_B, (data), (len), (data)+(len), (data)+(len)+1)
|
# define AddCrc14B(data, len) compute_crc(CRC_14443_B, (data), (len), (data)+(len), (data)+(len)+1)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define TOPAZ_STATIC_MEMORY (0x0F * 8) // 15 blocks with 8 Bytes each
|
static topaz_tag_t topaz_tag;
|
||||||
|
|
||||||
// a struct to describe a memory area which contains lock bits and the corresponding lockable memory area
|
|
||||||
typedef struct dynamic_lock_area {
|
|
||||||
struct dynamic_lock_area *next;
|
|
||||||
uint16_t byte_offset; // the address of the lock bits
|
|
||||||
uint16_t size_in_bits;
|
|
||||||
uint16_t first_locked_byte; // the address of the lockable area
|
|
||||||
uint16_t bytes_locked_per_bit;
|
|
||||||
} dynamic_lock_area_t;
|
|
||||||
|
|
||||||
static struct {
|
|
||||||
uint8_t HR01[2];
|
|
||||||
uint8_t uid[7];
|
|
||||||
uint16_t size;
|
|
||||||
uint8_t data_blocks[TOPAZ_STATIC_MEMORY / 8][8]; // this memory is always there
|
|
||||||
uint8_t *dynamic_memory; // this memory can be there
|
|
||||||
dynamic_lock_area_t *dynamic_lock_areas; // lock area descriptors
|
|
||||||
} topaz_tag;
|
|
||||||
|
|
||||||
static void topaz_switch_on_field(void) {
|
static void topaz_switch_on_field(void) {
|
||||||
SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_NO_SELECT | ISO14A_NO_DISCONNECT | ISO14A_TOPAZMODE | ISO14A_NO_RATS, 0, 0, NULL, 0);
|
SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_NO_SELECT | ISO14A_NO_DISCONNECT | ISO14A_TOPAZMODE | ISO14A_NO_RATS, 0, 0, NULL, 0);
|
||||||
|
@ -142,30 +124,68 @@ static int topaz_rall(uint8_t *uid, uint8_t *response) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// read a block (8 Bytes) of a selected Topaz tag.
|
// read a block (8 Bytes) of a selected Topaz tag.
|
||||||
static int topaz_read_block(uint8_t *uid, uint8_t blockno, uint8_t *block_data) {
|
static int topaz_read_block(uint8_t blockno, uint8_t *block_data) {
|
||||||
uint16_t resp_len = 0;
|
uint8_t atqa[2] = {0};
|
||||||
uint8_t read8_cmd[] = {TOPAZ_READ8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
uint8_t rid_response[8] = {0};
|
||||||
uint8_t read8_response[11] = {0};
|
int res = topaz_select(atqa, sizeof(atqa), rid_response, sizeof(rid_response), true);
|
||||||
|
if (res != PM3_SUCCESS) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
read8_cmd[1] = blockno;
|
if (atqa[1] != 0x0c && atqa[0] != 0x00) {
|
||||||
memcpy(&read8_cmd[10], uid, 4);
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
if (topaz_send_cmd(read8_cmd, sizeof(read8_cmd), read8_response, &resp_len, true) == PM3_ETIMEOUT) {
|
uint8_t *uid_echo = &rid_response[2];
|
||||||
|
uint8_t rall_response[124] = {0};
|
||||||
|
|
||||||
|
res = topaz_rall(uid_echo, rall_response);
|
||||||
|
if (res == PM3_ESOFT) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t read8_cmd[] = {TOPAZ_READ8, blockno, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
||||||
|
memcpy(&read8_cmd[10], uid_echo, 4);
|
||||||
|
|
||||||
|
uint16_t resp_len = 11;
|
||||||
|
uint8_t response[11] = {0};
|
||||||
|
|
||||||
|
if (topaz_send_cmd(read8_cmd, sizeof(read8_cmd), response, &resp_len, true) == PM3_ETIMEOUT) {
|
||||||
topaz_switch_off_field();
|
topaz_switch_off_field();
|
||||||
return PM3_ESOFT; // READ8 failed
|
return PM3_ESOFT; // READ8 failed
|
||||||
}
|
}
|
||||||
memcpy(block_data, &read8_response[1], 8);
|
memcpy(block_data, &response[1], 8);
|
||||||
return PM3_SUCCESS;
|
return PM3_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
// read a segment (16 blocks = 128 Bytes) of a selected Topaz tag. Works only for tags with dynamic memory.
|
// read a segment (16 blocks = 128 Bytes) of a selected Topaz tag. Works only for tags with dynamic memory.
|
||||||
static int topaz_read_segment(uint8_t *uid, uint8_t segno, uint8_t *segment_data) {
|
static int topaz_read_segment(uint8_t segno, uint8_t *segment_data) {
|
||||||
uint16_t resp_len = 0;
|
|
||||||
|
uint8_t atqa[2] = {0};
|
||||||
|
uint8_t rid_response[8] = {0};
|
||||||
|
int res = topaz_select(atqa, sizeof(atqa), rid_response, sizeof(rid_response), true);
|
||||||
|
if (res != PM3_SUCCESS) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (atqa[1] != 0x0c && atqa[0] != 0x00) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t *uid_echo = &rid_response[2];
|
||||||
|
uint8_t rall_response[124] = {0};
|
||||||
|
|
||||||
|
res = topaz_rall(uid_echo, rall_response);
|
||||||
|
if (res == PM3_ESOFT) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t resp_len = 131;
|
||||||
uint8_t rseg_cmd[] = {TOPAZ_RSEG, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
uint8_t rseg_cmd[] = {TOPAZ_RSEG, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
||||||
uint8_t rseg_response[131];
|
uint8_t rseg_response[131];
|
||||||
|
|
||||||
rseg_cmd[1] = segno << 4;
|
rseg_cmd[1] = segno << 4;
|
||||||
memcpy(&rseg_cmd[10], uid, 4);
|
memcpy(&rseg_cmd[10], uid_echo, 4);
|
||||||
|
|
||||||
if (topaz_send_cmd(rseg_cmd, sizeof(rseg_cmd), rseg_response, &resp_len, true) == PM3_ETIMEOUT) {
|
if (topaz_send_cmd(rseg_cmd, sizeof(rseg_cmd), rseg_response, &resp_len, true) == PM3_ETIMEOUT) {
|
||||||
topaz_switch_off_field();
|
topaz_switch_off_field();
|
||||||
|
@ -175,6 +195,55 @@ static int topaz_read_segment(uint8_t *uid, uint8_t segno, uint8_t *segment_data
|
||||||
return PM3_SUCCESS;
|
return PM3_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// write a block (8 Bytes) of a selected Topaz tag.
|
||||||
|
static int topaz_write_erase8_block(uint8_t blockno, uint8_t *block_data) {
|
||||||
|
|
||||||
|
uint8_t atqa[2] = {0};
|
||||||
|
uint8_t rid_response[8] = {0};
|
||||||
|
int res = topaz_select(atqa, sizeof(atqa), rid_response, sizeof(rid_response), true);
|
||||||
|
if (res != PM3_SUCCESS) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (atqa[1] != 0x0c && atqa[0] != 0x00) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t *uid_echo = &rid_response[2];
|
||||||
|
uint8_t rall_response[124] = {0};
|
||||||
|
|
||||||
|
res = topaz_rall(uid_echo, rall_response);
|
||||||
|
if (res == PM3_ESOFT) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t wr8_cmd[] = {TOPAZ_WRITE_E8, blockno, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
||||||
|
memcpy(wr8_cmd + 10, uid_echo, 4);
|
||||||
|
memcpy(wr8_cmd + 2, block_data, 8);
|
||||||
|
|
||||||
|
uint16_t resp_len = 11;
|
||||||
|
uint8_t response[11] = {0};
|
||||||
|
|
||||||
|
if (topaz_send_cmd(wr8_cmd, sizeof(wr8_cmd), response, &resp_len, true) == PM3_ETIMEOUT) {
|
||||||
|
topaz_switch_off_field();
|
||||||
|
return PM3_ESOFT; // WriteErase 8bytes failed
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resp_len != 11) {
|
||||||
|
return PM3_EFAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (blockno != response[0]) {
|
||||||
|
return PM3_EFAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (memcmp(block_data, response + 1, 8) == 0) {
|
||||||
|
return PM3_SUCCESS;
|
||||||
|
}
|
||||||
|
return PM3_ESOFT;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// search for the lock area descriptor for the lockable area including byteno
|
// search for the lock area descriptor for the lockable area including byteno
|
||||||
static dynamic_lock_area_t *get_dynamic_lock_area(uint16_t byteno) {
|
static dynamic_lock_area_t *get_dynamic_lock_area(uint16_t byteno) {
|
||||||
dynamic_lock_area_t *lock_area;
|
dynamic_lock_area_t *lock_area;
|
||||||
|
@ -223,25 +292,112 @@ static bool topaz_byte_is_locked(uint16_t byteno) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// read and print the Capability Container
|
static int topaz_set_cc_dynamic(uint8_t *data) {
|
||||||
static int topaz_print_CC(uint8_t *data) {
|
|
||||||
if (data[0] != 0xe1) {
|
if (data[0] != 0xE1) {
|
||||||
topaz_tag.size = TOPAZ_STATIC_MEMORY;
|
topaz_tag.size = TOPAZ_STATIC_MEMORY;
|
||||||
|
PrintAndLogEx(WARNING, "No Type 1 NDEF capability container found");
|
||||||
return PM3_ESOFT; // no NDEF message
|
return PM3_ESOFT; // no NDEF message
|
||||||
}
|
}
|
||||||
|
|
||||||
PrintAndLogEx(SUCCESS, "Capability Container: %02x %02x %02x %02x", data[0], data[1], data[2], data[3]);
|
// setting of dynamic memory and allocation of such memory.
|
||||||
PrintAndLogEx(SUCCESS, " %02x: NDEF Magic Number", data[0]);
|
|
||||||
PrintAndLogEx(SUCCESS, " %02x: version %d.%d supported by tag", data[1], (data[1] & 0xF0) >> 4, data[1] & 0x0f);
|
|
||||||
|
|
||||||
uint16_t memsize = (data[2] + 1) * 8;
|
uint16_t memsize = (data[2] + 1) * 8;
|
||||||
topaz_tag.size = memsize;
|
topaz_tag.size = memsize;
|
||||||
topaz_tag.dynamic_memory = calloc(memsize - TOPAZ_STATIC_MEMORY, sizeof(uint8_t));
|
topaz_tag.dynamic_memory = calloc(memsize - TOPAZ_STATIC_MEMORY, sizeof(uint8_t));
|
||||||
|
if (topaz_tag.dynamic_memory == NULL) {
|
||||||
|
return PM3_EMALLOC;
|
||||||
|
}
|
||||||
|
return PM3_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
PrintAndLogEx(SUCCESS, " %02x: Physical Memory Size of this tag: %d bytes", data[2], memsize);
|
// read and print the Capability Container
|
||||||
PrintAndLogEx(SUCCESS, " %02x: %s / %s", data[3],
|
static int topaz_print_CC(uint8_t *data) {
|
||||||
(data[3] & 0xF0) ? "(RFU)" : "Read access granted without any security",
|
|
||||||
(data[3] & 0x0F) == 0 ? "Write access granted without any security" : (data[3] & 0x0F) == 0x0F ? "No write access granted at all" : "(RFU)");
|
if (data[0] != 0xE1) {
|
||||||
|
topaz_tag.size = TOPAZ_STATIC_MEMORY;
|
||||||
|
PrintAndLogEx(WARNING, "No Type 1 NDEF capability container found");
|
||||||
|
return PM3_ESOFT; // no NDEF message
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//NFC Forum Type 1,2,3,4
|
||||||
|
//
|
||||||
|
// 4 has 1.1 (11)
|
||||||
|
|
||||||
|
// b7, b6 major version
|
||||||
|
// b5, b4 minor version
|
||||||
|
// b3, b2 read
|
||||||
|
// 00 always, 01 rfu, 10 proprietary, 11 rfu
|
||||||
|
// b1, b0 write
|
||||||
|
// 00 always, 01 rfo, 10 proprietary, 11 never
|
||||||
|
|
||||||
|
// vs
|
||||||
|
|
||||||
|
// NFC Forum Type 2 docs, where
|
||||||
|
// b7654 = major version
|
||||||
|
// b3219 = minor version
|
||||||
|
|
||||||
|
// CC write / read access
|
||||||
|
// (data[3] & 0xF0) ? "(RFU)" : "Read access granted without any security",
|
||||||
|
// (data[3] & 0x0F) == 0 ? "Write access granted without any security" : (data[3] & 0x0F) == 0x0F ? "No write access granted at all" : "(RFU)");
|
||||||
|
uint8_t cc_major = (data[1] & 0xF0) >> 4;
|
||||||
|
uint8_t cc_minor = (data[1] & 0x00);
|
||||||
|
|
||||||
|
uint8_t cc_write = data[3] & 0x0F;
|
||||||
|
uint8_t cc_read = (data[3] & 0xF0) >> 4;
|
||||||
|
|
||||||
|
const char *wStr;
|
||||||
|
switch (cc_write) {
|
||||||
|
case 0:
|
||||||
|
wStr = "Write access granted without any security";
|
||||||
|
break;
|
||||||
|
case 0xF:
|
||||||
|
wStr = "No write access";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
wStr = "RFU";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
const char *rStr;
|
||||||
|
switch (cc_read) {
|
||||||
|
case 0:
|
||||||
|
rStr = "Read access granted without any security";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
rStr = "RFU";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
PrintAndLogEx(SUCCESS, "Capability Container: %s", sprint_hex(data, 4));
|
||||||
|
PrintAndLogEx(SUCCESS, " %02X: NDEF Magic Number", data[0]);
|
||||||
|
|
||||||
|
// PrintAndLogEx(SUCCESS, " %02X : version %d.%d supported by tag", data[1], (data[1] & 0xF0) >> 4, data[1] & 0x0F);
|
||||||
|
PrintAndLogEx(SUCCESS, " %02X: version %d.%d supported by tag", data[1], cc_major, cc_minor);
|
||||||
|
PrintAndLogEx(SUCCESS, " : %s / %s", rStr, wStr);
|
||||||
|
|
||||||
|
PrintAndLogEx(SUCCESS, " %02X: Physical Memory Size: %d bytes", data[2], (data[2] + 1) * 8);
|
||||||
|
if (data[2] == 0x0E)
|
||||||
|
PrintAndLogEx(SUCCESS, " %02X: NDEF Memory Size: %d bytes", data[2], 120);
|
||||||
|
else if (data[2] == 0x1F)
|
||||||
|
PrintAndLogEx(SUCCESS, " %02X: NDEF Memory Size: %d bytes", data[2], 256);
|
||||||
|
else if (data[2] == 0xFF)
|
||||||
|
PrintAndLogEx(SUCCESS, " %02X: NDEF Memory Size: %d bytes", data[2], 2048);
|
||||||
|
|
||||||
|
uint8_t msb3 = (data[3] & 0xE0) >> 5;
|
||||||
|
uint8_t sf = (data[3] & 0x10) >> 4;
|
||||||
|
uint8_t lb = (data[3] & 0x08) >> 3;
|
||||||
|
uint8_t mlrule = (data[3] & 0x06) >> 1;
|
||||||
|
uint8_t mbread = (data[3] & 0x01);
|
||||||
|
|
||||||
|
PrintAndLogEx(SUCCESS, " %02X: Additional feature information", data[3]);
|
||||||
|
PrintAndLogEx(SUCCESS, " ^^");
|
||||||
|
PrintAndLogEx(SUCCESS, " 00000000");
|
||||||
|
PrintAndLogEx(SUCCESS, " xxx..... - %02X: RFU ( %s )", msb3, (msb3 == 0) ? _GREEN_("ok") : _RED_("fail"));
|
||||||
|
PrintAndLogEx(SUCCESS, " ...x.... - %02X: %s special frame", sf, (sf) ? "support" : "don\'t support");
|
||||||
|
PrintAndLogEx(SUCCESS, " ....x... - %02X: %s lock block", lb, (lb) ? "support" : "don\'t support");
|
||||||
|
PrintAndLogEx(SUCCESS, " .....xx. - %02X: RFU ( %s )", mlrule, (mlrule == 0) ? _GREEN_("ok") : _RED_("fail"));
|
||||||
|
PrintAndLogEx(SUCCESS, " .......x - %02X: IC %s multiple block reads", mbread, (mbread) ? "support" : "don\'t support");
|
||||||
|
PrintAndLogEx(SUCCESS,"");
|
||||||
|
|
||||||
return PM3_SUCCESS;
|
return PM3_SUCCESS;
|
||||||
}
|
}
|
||||||
|
@ -314,10 +470,8 @@ static void topaz_print_control_TLVs(uint8_t *memory) {
|
||||||
uint16_t bytes_locked_per_bit = 1 << (TLV_value[2] >> 4);
|
uint16_t bytes_locked_per_bit = 1 << (TLV_value[2] >> 4);
|
||||||
uint16_t area_start = pages_addr * bytes_per_page + byte_offset;
|
uint16_t area_start = pages_addr * bytes_per_page + byte_offset;
|
||||||
|
|
||||||
PrintAndLogEx(SUCCESS, "Lock Area of %d bits at byte offset 0x%04x. Each Lock Bit locks %d bytes.",
|
PrintAndLogEx(SUCCESS, "Lock Area of " _YELLOW_("%d") " bits at byte offset " _YELLOW_("0x%04x"), size_in_bits, area_start);
|
||||||
size_in_bits,
|
PrintAndLogEx(SUCCESS, "Each lock bit locks " _YELLOW_("%d") " bytes", bytes_locked_per_bit);
|
||||||
area_start,
|
|
||||||
bytes_locked_per_bit);
|
|
||||||
|
|
||||||
lock_TLV_present = true;
|
lock_TLV_present = true;
|
||||||
dynamic_lock_area_t *old = topaz_tag.dynamic_lock_areas;
|
dynamic_lock_area_t *old = topaz_tag.dynamic_lock_areas;
|
||||||
|
@ -352,7 +506,7 @@ static void topaz_print_control_TLVs(uint8_t *memory) {
|
||||||
uint8_t bytes_per_page = 1 << (TLV_value[2] & 0x0f);
|
uint8_t bytes_per_page = 1 << (TLV_value[2] & 0x0f);
|
||||||
uint16_t area_start = pages_addr * bytes_per_page + byte_offset;
|
uint16_t area_start = pages_addr * bytes_per_page + byte_offset;
|
||||||
|
|
||||||
PrintAndLogEx(SUCCESS, "Reserved Memory of %d bytes at byte offset 0x%02x.",
|
PrintAndLogEx(SUCCESS, "Reserved Memory... " _GREEN_("%d") " bytes at byte offset " _YELLOW_("0x%02x"),
|
||||||
size_in_bytes,
|
size_in_bytes,
|
||||||
area_start);
|
area_start);
|
||||||
|
|
||||||
|
@ -378,15 +532,15 @@ static void topaz_print_control_TLVs(uint8_t *memory) {
|
||||||
// read all of the dynamic memory
|
// read all of the dynamic memory
|
||||||
static int topaz_read_dynamic_data(void) {
|
static int topaz_read_dynamic_data(void) {
|
||||||
// first read the remaining block of segment 0
|
// first read the remaining block of segment 0
|
||||||
if (topaz_read_block(topaz_tag.uid, 0x0F, &topaz_tag.dynamic_memory[0]) == PM3_ESOFT) {
|
if (topaz_read_block(0x0F, &topaz_tag.dynamic_memory[0]) == PM3_ESOFT) {
|
||||||
PrintAndLogEx(ERR, "Error while reading dynamic memory block " _YELLOW_("%02x") ". Aborting...", 0x0F);
|
PrintAndLogEx(ERR, "Error while reading dynamic memory block " _YELLOW_("%02x") ". Aborting...", 0x0F);
|
||||||
return PM3_ESOFT;
|
return PM3_ESOFT;
|
||||||
}
|
}
|
||||||
|
|
||||||
// read the remaining segments
|
// read the remaining segments
|
||||||
uint8_t max_segment = topaz_tag.size / 128 - 1;
|
uint8_t max = topaz_tag.size / 128 - 1;
|
||||||
for (uint8_t segment = 1; segment <= max_segment; segment++) {
|
for (uint8_t segment = 1; segment <= max; segment++) {
|
||||||
if (topaz_read_segment(topaz_tag.uid, segment, &topaz_tag.dynamic_memory[(segment - 1) * 128 + 8]) == PM3_ESOFT) {
|
if (topaz_read_segment(segment, &topaz_tag.dynamic_memory[(segment - 1) * 128 + 8]) == PM3_ESOFT) {
|
||||||
PrintAndLogEx(ERR, "Error while reading dynamic memory block " _YELLOW_("%02x") ". Aborting...", segment);
|
PrintAndLogEx(ERR, "Error while reading dynamic memory block " _YELLOW_("%02x") ". Aborting...", segment);
|
||||||
return PM3_ESOFT;
|
return PM3_ESOFT;
|
||||||
}
|
}
|
||||||
|
@ -403,10 +557,10 @@ static void topaz_print_dynamic_data(void) {
|
||||||
|
|
||||||
PrintAndLogEx(SUCCESS, "Dynamic Data blocks:");
|
PrintAndLogEx(SUCCESS, "Dynamic Data blocks:");
|
||||||
|
|
||||||
if (topaz_read_dynamic_data() == 0) {
|
if (topaz_read_dynamic_data() == PM3_SUCCESS) {
|
||||||
|
|
||||||
PrintAndLogEx(NORMAL, "block# | offset | Data | Locked (y/n)");
|
PrintAndLogEx(SUCCESS, "block# | Data |lck");
|
||||||
PrintAndLogEx(NORMAL, "-------+--------+-------------------------+-------------");
|
PrintAndLogEx(SUCCESS, "-------+-------------------------+-------------");
|
||||||
|
|
||||||
char line[80];
|
char line[80];
|
||||||
for (uint16_t blockno = 0x0F; blockno < topaz_tag.size / 8; blockno++) {
|
for (uint16_t blockno = 0x0F; blockno < topaz_tag.size / 8; blockno++) {
|
||||||
|
@ -420,7 +574,7 @@ static void topaz_print_dynamic_data(void) {
|
||||||
}
|
}
|
||||||
lockbits[8] = '\0';
|
lockbits[8] = '\0';
|
||||||
|
|
||||||
PrintAndLogEx(NORMAL, " 0x%02x | 0x%04x | %s| %-3s", blockno, blockno * 8, line, lockbits);
|
PrintAndLogEx(SUCCESS, " 0x%02x | %s| %-3s", blockno, line, lockbits);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -429,6 +583,43 @@ static void topaz_print_lifecycle_state(uint8_t *data) {
|
||||||
// to be done
|
// to be done
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void printTopazDumpContents(uint8_t *dump, size_t size) {
|
||||||
|
|
||||||
|
topaz_tag_t *t = (topaz_tag_t *)dump;
|
||||||
|
|
||||||
|
// uses a global var for all
|
||||||
|
PrintAndLogEx(NORMAL, "");
|
||||||
|
PrintAndLogEx(SUCCESS, "Static data blocks :");
|
||||||
|
PrintAndLogEx(SUCCESS, "block# | data |lck| info");
|
||||||
|
PrintAndLogEx(SUCCESS, "------------+-------------------------+---+------------");
|
||||||
|
|
||||||
|
const char *block_info;
|
||||||
|
const char *topaz_ks[] = { "uid", "user", "rfu", "lock / otp" };
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i <= 0x0C; i++) {
|
||||||
|
|
||||||
|
if (i == 0)
|
||||||
|
block_info = topaz_ks[i];
|
||||||
|
else
|
||||||
|
block_info = topaz_ks[1];
|
||||||
|
|
||||||
|
const char *lockstr = (topaz_byte_is_locked(i * 8)) ? _RED_("x") : " ";
|
||||||
|
|
||||||
|
PrintAndLogEx(SUCCESS, " %3u / 0x%02x | %s| %s | %s",
|
||||||
|
i,
|
||||||
|
i,
|
||||||
|
sprint_hex(&t->data_blocks[i][0], 8),
|
||||||
|
lockstr,
|
||||||
|
block_info
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
PrintAndLogEx(SUCCESS, " %3u / 0x%02x | %s| | %s", 0x0D, 0x0D, sprint_hex(&t->data_blocks[0x0D][0], 8), topaz_ks[2]);
|
||||||
|
PrintAndLogEx(SUCCESS, " %3u / 0x%02x | %s| | %s", 0x0E, 0x0E, sprint_hex(&t->data_blocks[0x0E][0], 8), topaz_ks[3]);
|
||||||
|
PrintAndLogEx(SUCCESS, "------------+-------------------------+---+------------");
|
||||||
|
PrintAndLogEx(NORMAL, "");
|
||||||
|
}
|
||||||
|
|
||||||
static int CmdHFTopazReader(const char *Cmd) {
|
static int CmdHFTopazReader(const char *Cmd) {
|
||||||
CLIParserContext *ctx;
|
CLIParserContext *ctx;
|
||||||
CLIParserInit(&ctx, "hf topaz reader",
|
CLIParserInit(&ctx, "hf topaz reader",
|
||||||
|
@ -497,53 +688,21 @@ int CmdHFTopazInfo(const char *Cmd) {
|
||||||
PrintAndLogEx(SUCCESS, " ^^");
|
PrintAndLogEx(SUCCESS, " ^^");
|
||||||
|
|
||||||
PrintAndLogEx(SUCCESS, " %s", sprint_bin(topaz_tag.HR01, 1));
|
PrintAndLogEx(SUCCESS, " %s", sprint_bin(topaz_tag.HR01, 1));
|
||||||
PrintAndLogEx(SUCCESS, " x.......... TOPAZ tag ( %s ) - NDEF capable ( %s )",
|
PrintAndLogEx(SUCCESS, " ...x.... - %s / %s",
|
||||||
(topaz_tag.HR01[0] & 0xF0) == 0x10 ? _GREEN_("yes") : _RED_("no"),
|
(topaz_tag.HR01[0] & 0xF0) == 0x10 ? _GREEN_("TOPAZ tag") : "",
|
||||||
(topaz_tag.HR01[0] & 0xF0) == 0x10 ? _GREEN_("yes") : _RED_("no")
|
(topaz_tag.HR01[0] & 0xF0) == 0x10 ? _GREEN_("Type 1 NDEF") : ""
|
||||||
);
|
);
|
||||||
PrintAndLogEx(SUCCESS, " x... %s memory map", ((topaz_tag.HR01[0] & 0x0F) == 0x01) ? "Static" : "Dynamic" );
|
PrintAndLogEx(SUCCESS, " .......x - %s memory map", ((topaz_tag.HR01[0] & 0x0F) == 0x01) ? "Static" : "Dynamic" );
|
||||||
PrintAndLogEx(NORMAL, "");
|
PrintAndLogEx(SUCCESS, "");
|
||||||
|
PrintAndLogEx(SUCCESS, " Lock bytes... %02X%02X",
|
||||||
PrintAndLogEx(SUCCESS, "Static Data blocks " _YELLOW_("0x00") " to " _YELLOW_("0x0C")":");
|
topaz_tag.data_blocks[0x0e][0],
|
||||||
PrintAndLogEx(NORMAL, "block# | offset | Data | Locked");
|
topaz_tag.data_blocks[0x0e][1]
|
||||||
PrintAndLogEx(NORMAL, "-------+--------+-------------------------+------------");
|
);
|
||||||
|
|
||||||
char line[80];
|
PrintAndLogEx(SUCCESS, " OTP.......... %s", sprint_hex(&topaz_tag.data_blocks[0x0e][2], 6));
|
||||||
for (uint8_t i = 0; i <= 0x0C; i++) {
|
|
||||||
|
|
||||||
char lockbits[9];
|
|
||||||
for (uint8_t j = 0; j < 8; j++) {
|
|
||||||
int offset = 3 * j;
|
|
||||||
snprintf(line + offset, sizeof(line) - offset, "%02x ", topaz_tag.data_blocks[i][j] /*rall_response[2 + 8*i + j]*/);
|
|
||||||
lockbits[j] = topaz_byte_is_locked(i * 8 + j) ? 'y' : 'n';
|
|
||||||
}
|
|
||||||
lockbits[8] = '\0';
|
|
||||||
PrintAndLogEx(NORMAL, " 0x%02x | 0x%02x | %s| %-3s", i, i * 8, line, lockbits);
|
|
||||||
}
|
|
||||||
|
|
||||||
PrintAndLogEx(NORMAL, "");
|
|
||||||
PrintAndLogEx(SUCCESS, "Static Reserved block " _YELLOW_("0x0D")":");
|
|
||||||
|
|
||||||
for (uint8_t j = 0; j < 8; j++) {
|
|
||||||
int offset = 3 * j;
|
|
||||||
snprintf(line + offset, sizeof(line) - offset, "%02x ", topaz_tag.data_blocks[0x0d][j]);
|
|
||||||
}
|
|
||||||
|
|
||||||
PrintAndLogEx(NORMAL, "-------+--------+-------------------------+------------");
|
|
||||||
PrintAndLogEx(NORMAL, " 0x%02x | 0x%02x | %s| %-3s", 0x0d, 0x0d * 8, line, "n/a");
|
|
||||||
PrintAndLogEx(NORMAL, "");
|
|
||||||
|
|
||||||
PrintAndLogEx(SUCCESS, "Static Lockbits and OTP Bytes:");
|
|
||||||
|
|
||||||
for (uint8_t j = 0; j < 8; j++) {
|
|
||||||
int offset = 3 * j;
|
|
||||||
snprintf(line + offset, sizeof(line) - offset, "%02x ", topaz_tag.data_blocks[0x0e][j]);
|
|
||||||
}
|
|
||||||
|
|
||||||
PrintAndLogEx(NORMAL, "-------+--------+-------------------------+------------");
|
|
||||||
PrintAndLogEx(NORMAL, " 0x%02x | 0x%02x | %s| %-3s", 0x0e, 0x0e * 8, line, "n/a");
|
|
||||||
PrintAndLogEx(NORMAL, "");
|
PrintAndLogEx(NORMAL, "");
|
||||||
|
|
||||||
|
PrintAndLogEx(INFO, "--- " _CYAN_("NDEF configuration") " ---------------------------");
|
||||||
status = topaz_print_CC(&topaz_tag.data_blocks[1][0]);
|
status = topaz_print_CC(&topaz_tag.data_blocks[1][0]);
|
||||||
if (status == PM3_ESOFT) {
|
if (status == PM3_ESOFT) {
|
||||||
PrintAndLogEx(SUCCESS, "No NDEF message data present");
|
PrintAndLogEx(SUCCESS, "No NDEF message data present");
|
||||||
|
@ -555,16 +714,6 @@ int CmdHFTopazInfo(const char *Cmd) {
|
||||||
topaz_print_control_TLVs(&topaz_tag.data_blocks[1][4]);
|
topaz_print_control_TLVs(&topaz_tag.data_blocks[1][4]);
|
||||||
|
|
||||||
PrintAndLogEx(NORMAL, "");
|
PrintAndLogEx(NORMAL, "");
|
||||||
topaz_print_dynamic_data();
|
|
||||||
|
|
||||||
topaz_print_lifecycle_state(&topaz_tag.data_blocks[1][0]);
|
|
||||||
|
|
||||||
if (fnlen != 0) {
|
|
||||||
saveFile(filename, ".bin", &topaz_tag.data_blocks[1][0], TOPAZ_STATIC_MEMORY);
|
|
||||||
}
|
|
||||||
NDEFDecodeAndPrint(&topaz_tag.data_blocks[1][0], TOPAZ_STATIC_MEMORY, true);
|
|
||||||
|
|
||||||
PrintAndLogEx(INFO, "-------------------------------------------------------------");
|
|
||||||
topaz_switch_off_field();
|
topaz_switch_off_field();
|
||||||
return PM3_SUCCESS;
|
return PM3_SUCCESS;
|
||||||
}
|
}
|
||||||
|
@ -585,7 +734,7 @@ static int CmdHFTopazSim(const char *Cmd) {
|
||||||
return PM3_SUCCESS;
|
return PM3_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int CmdHFTopazCmdRaw(const char *Cmd) {
|
static int CmdHFTopazRaw(const char *Cmd) {
|
||||||
CLIParserContext *ctx;
|
CLIParserContext *ctx;
|
||||||
CLIParserInit(&ctx, "hf topaz raw",
|
CLIParserInit(&ctx, "hf topaz raw",
|
||||||
"Send raw hex data to Topaz tags",
|
"Send raw hex data to Topaz tags",
|
||||||
|
@ -624,16 +773,213 @@ static int CmdHFTopazSniff(const char *Cmd) {
|
||||||
return PM3_SUCCESS;
|
return PM3_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int CmdHFTopazDump(const char *Cmd) {
|
||||||
|
|
||||||
|
CLIParserContext *ctx;
|
||||||
|
CLIParserInit(&ctx, "hf topaz dump",
|
||||||
|
"Dump TOPAZ tag to binary file\n"
|
||||||
|
"If no <name> given, UID will be used as filename",
|
||||||
|
"hf topaz dump\n");
|
||||||
|
|
||||||
|
void *argtable[] = {
|
||||||
|
arg_param_begin,
|
||||||
|
arg_str0("f", "file", "<fn>", "filename of dump"),
|
||||||
|
arg_param_end
|
||||||
|
};
|
||||||
|
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
||||||
|
|
||||||
|
int fnlen = 0;
|
||||||
|
char filename[FILE_PATH_SIZE] = {0};
|
||||||
|
CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen);
|
||||||
|
CLIParserFree(ctx);
|
||||||
|
|
||||||
|
int status = readTopazUid(false, false);
|
||||||
|
if (status != PM3_SUCCESS) {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
printTopazDumpContents((uint8_t*)&topaz_tag, sizeof(topaz_tag_t));
|
||||||
|
|
||||||
|
bool set_dynamic = false;
|
||||||
|
if (topaz_set_cc_dynamic(&topaz_tag.data_blocks[1][0]) == PM3_SUCCESS) {
|
||||||
|
set_dynamic = true;
|
||||||
|
|
||||||
|
topaz_print_dynamic_data();
|
||||||
|
|
||||||
|
topaz_print_lifecycle_state(&topaz_tag.data_blocks[1][0]);
|
||||||
|
|
||||||
|
NDEFDecodeAndPrint(&topaz_tag.data_blocks[1][4],
|
||||||
|
(topaz_tag.HR01[0] == 1) ? (12 * 8) : 476
|
||||||
|
, true
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
PrintAndLogEx(INFO, "-------------------------------------------------------------");
|
||||||
|
topaz_switch_off_field();
|
||||||
|
|
||||||
|
// user supplied filename?
|
||||||
|
if (fnlen < 1) {
|
||||||
|
PrintAndLogEx(INFO, "Using UID as filename");
|
||||||
|
strcat(filename, "hf-topaz-");
|
||||||
|
FillFileNameByUID(filename, topaz_tag.uid, "-dump", sizeof(topaz_tag.uid));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (topaz_tag.size)
|
||||||
|
pm3_save_dump(filename, (uint8_t *)&topaz_tag, sizeof(topaz_tag_t) + topaz_tag.size, jsfTopaz, TOPAZ_BLOCK_SIZE);
|
||||||
|
else
|
||||||
|
pm3_save_dump(filename, (uint8_t *)&topaz_tag, sizeof(topaz_tag_t), jsfTopaz, TOPAZ_BLOCK_SIZE);
|
||||||
|
|
||||||
|
if (set_dynamic) {
|
||||||
|
free(topaz_tag.dynamic_memory);
|
||||||
|
}
|
||||||
|
|
||||||
|
return PM3_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int CmdHFTopazView(const char *Cmd) {
|
||||||
|
CLIParserContext *ctx;
|
||||||
|
CLIParserInit(&ctx, "hf topaz view",
|
||||||
|
"Print a Topaz tag dump file (bin/eml/json)",
|
||||||
|
"hf topaz view -f hf-topaz-04010203-dump.bin");
|
||||||
|
|
||||||
|
void *argtable[] = {
|
||||||
|
arg_param_begin,
|
||||||
|
arg_str1("f", "file", "<fn>", "filename of dump (bin/eml/json)"),
|
||||||
|
arg_param_end
|
||||||
|
};
|
||||||
|
CLIExecWithReturn(ctx, Cmd, argtable, false);
|
||||||
|
int fnlen = 0;
|
||||||
|
char filename[FILE_PATH_SIZE];
|
||||||
|
CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen);
|
||||||
|
CLIParserFree(ctx);
|
||||||
|
|
||||||
|
// read dump file
|
||||||
|
uint8_t *dump = NULL;
|
||||||
|
size_t bytes_read = TOPAZ_MAX_SIZE;
|
||||||
|
int res = pm3_load_dump(filename, (void **)&dump, &bytes_read, sizeof(topaz_tag_t) + TOPAZ_MAX_SIZE);
|
||||||
|
if (res != PM3_SUCCESS) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
printTopazDumpContents(dump, bytes_read);
|
||||||
|
|
||||||
|
if (topaz_set_cc_dynamic(&topaz_tag.data_blocks[1][0]) == PM3_SUCCESS) {
|
||||||
|
|
||||||
|
topaz_print_dynamic_data();
|
||||||
|
|
||||||
|
topaz_print_lifecycle_state(&topaz_tag.data_blocks[1][0]);
|
||||||
|
|
||||||
|
NDEFDecodeAndPrint(&topaz_tag.data_blocks[1][4],
|
||||||
|
(topaz_tag.HR01[0] == 1) ? (12 * 8) : 476
|
||||||
|
, true
|
||||||
|
);
|
||||||
|
|
||||||
|
PrintAndLogEx(INFO, "%s", sprint_hex(&topaz_tag.data_blocks[1][4], (12*8)));
|
||||||
|
|
||||||
|
free(topaz_tag.dynamic_memory);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(dump);
|
||||||
|
return PM3_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read single block
|
||||||
|
static int CmdHFTopazRdBl(const char *Cmd) {
|
||||||
|
|
||||||
|
CLIParserContext *ctx;
|
||||||
|
CLIParserInit(&ctx, "hf topaz rdbl",
|
||||||
|
"Read a block",
|
||||||
|
"hf topaz rdbl -b 7\n"
|
||||||
|
);
|
||||||
|
|
||||||
|
void *argtable[] = {
|
||||||
|
arg_param_begin,
|
||||||
|
arg_int1("b", "block", "<dec>", "Block number to write"),
|
||||||
|
arg_param_end
|
||||||
|
};
|
||||||
|
CLIExecWithReturn(ctx, Cmd, argtable, false);
|
||||||
|
int blockno = arg_get_int_def(ctx, 1, -1);
|
||||||
|
CLIParserFree(ctx);
|
||||||
|
|
||||||
|
if (blockno < 0) {
|
||||||
|
PrintAndLogEx(WARNING, "Wrong block number");
|
||||||
|
return PM3_EINVARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
// send read block
|
||||||
|
uint8_t data[8] = {0};
|
||||||
|
int res = topaz_read_block(blockno, data);
|
||||||
|
if (res == PM3_SUCCESS) {
|
||||||
|
PrintAndLogEx(SUCCESS, "Block: %0d (0x%02X) [ %s]", blockno, blockno, sprint_hex(data, sizeof(data)));
|
||||||
|
}
|
||||||
|
|
||||||
|
topaz_switch_off_field();
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write single block
|
||||||
|
static int CmdHFTopazWrBl(const char *Cmd) {
|
||||||
|
|
||||||
|
CLIParserContext *ctx;
|
||||||
|
CLIParserInit(&ctx, "hf topaz wrbl",
|
||||||
|
"Write a block",
|
||||||
|
"hf topaz wrbl -b 7 -d 1122334455667788\n"
|
||||||
|
);
|
||||||
|
|
||||||
|
void *argtable[] = {
|
||||||
|
arg_param_begin,
|
||||||
|
arg_int1("b", "block", "<dec>", "Block number to write"),
|
||||||
|
arg_str1("d", "data", "<hex>", "Block data (8 hex bytes)"),
|
||||||
|
arg_param_end
|
||||||
|
};
|
||||||
|
CLIExecWithReturn(ctx, Cmd, argtable, false);
|
||||||
|
|
||||||
|
int blockno = arg_get_int_def(ctx, 1, -1);
|
||||||
|
|
||||||
|
int dlen = 0;
|
||||||
|
uint8_t data[8] = {0x00};
|
||||||
|
CLIGetHexWithReturn(ctx, 2, data, &dlen);
|
||||||
|
|
||||||
|
CLIParserFree(ctx);
|
||||||
|
|
||||||
|
if (blockno < 0) {
|
||||||
|
PrintAndLogEx(WARNING, "Wrong block number");
|
||||||
|
return PM3_EINVARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dlen != 8 ) {
|
||||||
|
PrintAndLogEx(WARNING, "Wrong data length. Expect 8, got %d", dlen);
|
||||||
|
return PM3_EINVARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
PrintAndLogEx(INFO, "Block: %0d (0x%02X) [ %s]", blockno, blockno, sprint_hex(data, dlen));
|
||||||
|
|
||||||
|
// send write Block
|
||||||
|
int res = topaz_write_erase8_block(blockno, data);
|
||||||
|
|
||||||
|
if (res == PM3_SUCCESS) {
|
||||||
|
PrintAndLogEx(SUCCESS, "Write ( " _GREEN_("ok") " )" );
|
||||||
|
} else {
|
||||||
|
PrintAndLogEx(WARNING, "Write ( " _RED_("fail") " )" );
|
||||||
|
}
|
||||||
|
|
||||||
|
topaz_switch_off_field();
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
static int CmdHelp(const char *Cmd);
|
static int CmdHelp(const char *Cmd);
|
||||||
|
|
||||||
static command_t CommandTable[] = {
|
static command_t CommandTable[] = {
|
||||||
{"help", CmdHelp, AlwaysAvailable, "This help"},
|
{"help", CmdHelp, AlwaysAvailable, "This help"},
|
||||||
{"list", CmdHFTopazList, AlwaysAvailable, "List Topaz history"},
|
{"dump", CmdHFTopazDump, IfPm3Iso14443a, "Dump TOPAZ family tag to file"},
|
||||||
{"info", CmdHFTopazInfo, IfPm3Iso14443a, "Tag information"},
|
{"list", CmdHFTopazList, AlwaysAvailable, "List Topaz history"},
|
||||||
{"reader", CmdHFTopazReader, IfPm3Iso14443a, "Act like a Topaz reader"},
|
{"info", CmdHFTopazInfo, IfPm3Iso14443a, "Tag information"},
|
||||||
{"sim", CmdHFTopazSim, IfPm3Iso14443a, "Simulate Topaz tag"},
|
{"reader", CmdHFTopazReader, IfPm3Iso14443a, "Act like a Topaz reader"},
|
||||||
{"sniff", CmdHFTopazSniff, IfPm3Iso14443a, "Sniff Topaz reader-tag communication"},
|
{"sim", CmdHFTopazSim, IfPm3Iso14443a, "Simulate Topaz tag"},
|
||||||
{"raw", CmdHFTopazCmdRaw, IfPm3Iso14443a, "Send raw hex data to tag"},
|
{"sniff", CmdHFTopazSniff, IfPm3Iso14443a, "Sniff Topaz reader-tag communication"},
|
||||||
|
{"raw", CmdHFTopazRaw, IfPm3Iso14443a, "Send raw hex data to tag"},
|
||||||
|
{"rdbl", CmdHFTopazRdBl, IfPm3Iso14443a, "Read block"},
|
||||||
|
{"view", CmdHFTopazView, AlwaysAvailable, "Display content from tag dump file"},
|
||||||
|
{"wrbl", CmdHFTopazWrBl, IfPm3Iso14443a, "Write block"},
|
||||||
{NULL, NULL, 0, NULL}
|
{NULL, NULL, 0, NULL}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -21,8 +21,32 @@
|
||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
|
||||||
|
|
||||||
|
#define TOPAZ_STATIC_MEMORY (0x0F * 8) // 15 blocks with 8 Bytes each
|
||||||
|
#define TOPAZ_BLOCK_SIZE 8
|
||||||
|
#define TOPAZ_MAX_SIZE 512
|
||||||
|
|
||||||
|
// a struct to describe a memory area which contains lock bits and the corresponding lockable memory area
|
||||||
|
typedef struct dynamic_lock_area_s {
|
||||||
|
struct dynamic_lock_area_s *next;
|
||||||
|
uint16_t byte_offset; // the address of the lock bits
|
||||||
|
uint16_t size_in_bits;
|
||||||
|
uint16_t first_locked_byte; // the address of the lockable area
|
||||||
|
uint16_t bytes_locked_per_bit;
|
||||||
|
} dynamic_lock_area_t;
|
||||||
|
|
||||||
|
typedef struct topaz_tag_s {
|
||||||
|
uint8_t HR01[2];
|
||||||
|
uint8_t uid[7];
|
||||||
|
uint16_t size;
|
||||||
|
uint8_t data_blocks[TOPAZ_STATIC_MEMORY / 8][8]; // this memory is always there
|
||||||
|
uint8_t *dynamic_memory; // this memory can be there
|
||||||
|
dynamic_lock_area_t *dynamic_lock_areas; // lock area descriptors
|
||||||
|
} topaz_tag_t;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int CmdHFTopaz(const char *Cmd);
|
int CmdHFTopaz(const char *Cmd);
|
||||||
int CmdHFTopazInfo(const char *Cmd);
|
int CmdHFTopazInfo(const char *Cmd);
|
||||||
|
|
||||||
int readTopazUid(bool loop, bool verbose);
|
int readTopazUid(bool loop, bool verbose);
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -28,6 +28,7 @@
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "cmdhficlass.h" // pagemap
|
#include "cmdhficlass.h" // pagemap
|
||||||
#include "protocols.h" // iclass defines
|
#include "protocols.h" // iclass defines
|
||||||
|
#include "cmdhftopaz.h" // TOPAZ defines
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include "scandir.h"
|
#include "scandir.h"
|
||||||
|
@ -632,6 +633,24 @@ int saveFileJSONex(const char *preferredName, JSONFileType ftype, uint8_t *data,
|
||||||
(*callback)(root);
|
(*callback)(root);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case jsfTopaz: {
|
||||||
|
topaz_tag_t *tag = (topaz_tag_t *)(void *) data;
|
||||||
|
JsonSaveStr(root, "FileType", "topaz");
|
||||||
|
JsonSaveBufAsHexCompact(root, "$.Card.UID", tag->uid, sizeof(tag->uid));
|
||||||
|
JsonSaveBufAsHexCompact(root, "$.Card.H0R1", tag->HR01, sizeof(tag->HR01));
|
||||||
|
JsonSaveBufAsHexCompact(root, "$.Card.Size", (uint8_t *)&(tag->size), 2);
|
||||||
|
for (size_t i = 0; i < TOPAZ_STATIC_MEMORY / 8; i++) {
|
||||||
|
char path[PATH_MAX_LENGTH] = {0};
|
||||||
|
snprintf(path, sizeof(path), "$.blocks.%zu", i);
|
||||||
|
JsonSaveBufAsHexCompact(root, path, &tag->data_blocks[i][0], TOPAZ_BLOCK_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ICEMAN todo: add dynamic memory.
|
||||||
|
// uint16_z Size
|
||||||
|
// uint8_t *dynamic_memory;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1278,6 +1297,38 @@ int loadFileJSONex(const char *preferredName, void *data, size_t maxdatalen, siz
|
||||||
JsonLoadBufAsHex(root, "$.raw", udata, maxdatalen, datalen);
|
JsonLoadBufAsHex(root, "$.raw", udata, maxdatalen, datalen);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!strcmp(ctype, "topaz")) {
|
||||||
|
|
||||||
|
topaz_tag_t *mem = (topaz_tag_t *)udata;
|
||||||
|
JsonLoadBufAsHex(root, "$.Card.UID", mem->uid, sizeof(mem->uid), datalen);
|
||||||
|
JsonLoadBufAsHex(root, "$.Card.HR01", mem->HR01, sizeof(mem->HR01), datalen);
|
||||||
|
JsonLoadBufAsHex(root, "$.Card.Size", (uint8_t *)&(mem->size), 2, datalen);
|
||||||
|
|
||||||
|
size_t sptr = 0;
|
||||||
|
for (int i = 0; i < (TOPAZ_STATIC_MEMORY / 8); i++) {
|
||||||
|
|
||||||
|
if (sptr + TOPAZ_BLOCK_SIZE > maxdatalen) {
|
||||||
|
retval = PM3_EMALLOC;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
char blocks[30] = {0};
|
||||||
|
snprintf(blocks, sizeof(blocks), "$.blocks.%d", i);
|
||||||
|
|
||||||
|
size_t len = 0;
|
||||||
|
JsonLoadBufAsHex(root, blocks, &mem->data_blocks[sptr][0], TOPAZ_BLOCK_SIZE, &len);
|
||||||
|
if (!len)
|
||||||
|
break;
|
||||||
|
|
||||||
|
sptr += len;
|
||||||
|
|
||||||
|
// ICEMAN todo: add dynamic memory.
|
||||||
|
// uint16_z Size
|
||||||
|
// uint8_t *dynamic_memory;
|
||||||
|
}
|
||||||
|
|
||||||
|
*datalen += sptr;
|
||||||
|
}
|
||||||
out:
|
out:
|
||||||
|
|
||||||
if (callback != NULL) {
|
if (callback != NULL) {
|
||||||
|
|
|
@ -48,6 +48,7 @@ typedef enum {
|
||||||
jsfEM4x50,
|
jsfEM4x50,
|
||||||
jsfFido,
|
jsfFido,
|
||||||
jsfFudan,
|
jsfFudan,
|
||||||
|
jsfTopaz,
|
||||||
} JSONFileType;
|
} JSONFileType;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
|
|
@ -1036,21 +1036,22 @@ int NDEFDecodeAndPrint(uint8_t *ndef, size_t ndefLen, bool verbose) {
|
||||||
case 0x01: {
|
case 0x01: {
|
||||||
indx++;
|
indx++;
|
||||||
uint16_t len = ndefTLVGetLength(&ndef[indx], &indx);
|
uint16_t len = ndefTLVGetLength(&ndef[indx], &indx);
|
||||||
|
PrintAndLogEx(NORMAL, "");
|
||||||
PrintAndLogEx(SUCCESS, "--- " _CYAN_("NDEF Lock Control") " ---");
|
PrintAndLogEx(SUCCESS, "--- " _CYAN_("NDEF Lock Control") " ---");
|
||||||
if (len != 3) {
|
if (len != 3) {
|
||||||
PrintAndLogEx(WARNING, "NDEF Lock Control block size must be 3 instead of %d.", len);
|
PrintAndLogEx(WARNING, "NDEF Lock Control block size must be 3 instead of %d", len);
|
||||||
} else {
|
} else {
|
||||||
uint8_t pages_addr = (ndef[indx] >> 4) & 0x0f;
|
uint8_t pages_addr = (ndef[indx] >> 4) & 0x0f;
|
||||||
uint8_t byte_offset = ndef[indx] & 0x0f;
|
uint8_t byte_offset = ndef[indx] & 0x0f;
|
||||||
uint8_t Size = ndef[indx + 1];
|
uint8_t Size = ndef[indx + 1];
|
||||||
uint8_t BytesLockedPerLockBit = (ndef[indx + 2] >> 4) & 0x0f;
|
uint8_t BytesLockedPerLockBit = (ndef[indx + 2] >> 4) & 0x0f;
|
||||||
uint8_t bytes_per_page = ndef[indx + 2] & 0x0f;
|
uint8_t bytes_per_page = ndef[indx + 2] & 0x0f;
|
||||||
PrintAndLogEx(SUCCESS, " Pages addr (number of pages)... %d", pages_addr);
|
PrintAndLogEx(SUCCESS, " Pages addr (number of pages).... %d", pages_addr);
|
||||||
PrintAndLogEx(SUCCESS, "Byte offset (number of bytes)... %d", byte_offset);
|
PrintAndLogEx(SUCCESS, " Byte offset (number of bytes)... %d", byte_offset);
|
||||||
PrintAndLogEx(SUCCESS, "Size in bits of the lock area %d. bytes approx %d", Size, Size / 8);
|
PrintAndLogEx(SUCCESS, " Lock Area size in bits.......... %d ( %d bytes )", Size, Size / 8);
|
||||||
PrintAndLogEx(SUCCESS, " Number of bytes / page... %d", bytes_per_page);
|
PrintAndLogEx(SUCCESS, " Number of bytes / page... %d", bytes_per_page);
|
||||||
PrintAndLogEx(SUCCESS, "Bytes Locked Per LockBit");
|
PrintAndLogEx(SUCCESS, " Bytes Locked Per LockBit");
|
||||||
PrintAndLogEx(SUCCESS, " number of bytes that each dynamic lock bit is able to lock: %d", BytesLockedPerLockBit);
|
PrintAndLogEx(SUCCESS, " number of bytes that each dynamic lock bit locks... %d", BytesLockedPerLockBit);
|
||||||
}
|
}
|
||||||
indx += len;
|
indx += len;
|
||||||
break;
|
break;
|
||||||
|
@ -1058,18 +1059,19 @@ int NDEFDecodeAndPrint(uint8_t *ndef, size_t ndefLen, bool verbose) {
|
||||||
case 0x02: {
|
case 0x02: {
|
||||||
indx++;
|
indx++;
|
||||||
uint16_t len = ndefTLVGetLength(&ndef[indx], &indx);
|
uint16_t len = ndefTLVGetLength(&ndef[indx], &indx);
|
||||||
|
PrintAndLogEx(NORMAL, "");
|
||||||
PrintAndLogEx(SUCCESS, "--- " _CYAN_("NDEF Memory Control") " ---");
|
PrintAndLogEx(SUCCESS, "--- " _CYAN_("NDEF Memory Control") " ---");
|
||||||
if (len != 3) {
|
if (len != 3) {
|
||||||
PrintAndLogEx(WARNING, "NDEF Memory Control block size must be 3 instead of %d.", len);
|
PrintAndLogEx(WARNING, "NDEF Memory Control block size must be 3 instead of %d", len);
|
||||||
} else {
|
} else {
|
||||||
uint8_t pages_addr = (ndef[indx] >> 4) & 0x0f;
|
uint8_t pages_addr = (ndef[indx] >> 4) & 0x0f;
|
||||||
uint8_t byte_offset = ndef[indx] & 0x0f;
|
uint8_t byte_offset = ndef[indx] & 0x0f;
|
||||||
uint8_t Size = ndef[indx + 1];
|
uint8_t Size = ndef[indx + 1];
|
||||||
uint8_t bytes_per_page = ndef[indx + 2] & 0x0f;
|
uint8_t bytes_per_page = ndef[indx + 2] & 0x0f;
|
||||||
PrintAndLogEx(SUCCESS, " Pages addr (number of pages) : %d", pages_addr);
|
PrintAndLogEx(SUCCESS, "Pages addr (number of pages).... %d", pages_addr);
|
||||||
PrintAndLogEx(SUCCESS, "Byte offset (number of bytes) : %d", byte_offset);
|
PrintAndLogEx(SUCCESS, "Byte offset (number of bytes)... %d", byte_offset);
|
||||||
PrintAndLogEx(SUCCESS, "Size in bits of the reserved area : %d. bytes approx: %d", Size, Size / 8);
|
PrintAndLogEx(SUCCESS, "Reserved area size in bits...... %d ( %d bytes )", Size, Size / 8);
|
||||||
PrintAndLogEx(SUCCESS, " Number of bytes / page : %d", bytes_per_page);
|
PrintAndLogEx(SUCCESS, " Number of bytes / page... %d", bytes_per_page);
|
||||||
}
|
}
|
||||||
indx += len;
|
indx += len;
|
||||||
break;
|
break;
|
||||||
|
@ -1077,6 +1079,7 @@ int NDEFDecodeAndPrint(uint8_t *ndef, size_t ndefLen, bool verbose) {
|
||||||
case 0x03: {
|
case 0x03: {
|
||||||
indx++;
|
indx++;
|
||||||
uint16_t len = ndefTLVGetLength(&ndef[indx], &indx);
|
uint16_t len = ndefTLVGetLength(&ndef[indx], &indx);
|
||||||
|
PrintAndLogEx(NORMAL, "");
|
||||||
PrintAndLogEx(SUCCESS, "--- " _CYAN_("NDEF Message") " ---");
|
PrintAndLogEx(SUCCESS, "--- " _CYAN_("NDEF Message") " ---");
|
||||||
if (len == 0) {
|
if (len == 0) {
|
||||||
PrintAndLogEx(SUCCESS, "Found NDEF message w zero length");
|
PrintAndLogEx(SUCCESS, "Found NDEF message w zero length");
|
||||||
|
@ -1094,6 +1097,7 @@ int NDEFDecodeAndPrint(uint8_t *ndef, size_t ndefLen, bool verbose) {
|
||||||
case 0xfd: {
|
case 0xfd: {
|
||||||
indx++;
|
indx++;
|
||||||
uint16_t len = ndefTLVGetLength(&ndef[indx], &indx);
|
uint16_t len = ndefTLVGetLength(&ndef[indx], &indx);
|
||||||
|
PrintAndLogEx(NORMAL, "");
|
||||||
PrintAndLogEx(SUCCESS, "--- " _CYAN_("Proprietary info") " ---");
|
PrintAndLogEx(SUCCESS, "--- " _CYAN_("Proprietary info") " ---");
|
||||||
PrintAndLogEx(SUCCESS, " Can't decode, skipping %d bytes", len);
|
PrintAndLogEx(SUCCESS, " Can't decode, skipping %d bytes", len);
|
||||||
indx += len;
|
indx += len;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue