mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2026-08-28 07:24:14 +00:00
Merge pull request #3402 from kormax/ask-cts-improvements
ASK CTS improvements
This commit is contained in:
@@ -3,6 +3,7 @@ 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...
|
||||
|
||||
## [unreleased][unreleased]
|
||||
- Added `hf 14b ctrdbl` and `hf 14b ctdump` commands for interacting with ASK CTS tags (@kormax)
|
||||
- Fixed `hf legic migrate` failing to parse the optional DCF argument as hex (@IdanHo)
|
||||
- Add support for parsing Finnish Helsinki Regional Transport (HRT) travel cards (@sanduuz)
|
||||
- Added standalone mode `HF_DOEGOX_COMMIT`: DESFire suspended commit without relay (@doegox)
|
||||
|
||||
+1
-1
@@ -1818,7 +1818,7 @@ static int iso14443b_select_cts_card(iso14b_cts_card_select_t *card) {
|
||||
}
|
||||
|
||||
if (card) {
|
||||
// pc. fc Product code, Facility code
|
||||
// REQT response payload: Product code, FAB code
|
||||
card->pc = r[0];
|
||||
card->fc = r[1];
|
||||
}
|
||||
|
||||
+241
-14
@@ -48,6 +48,9 @@
|
||||
|
||||
// for static arrays
|
||||
#define ST25TB_SR_BLOCK_SIZE 4
|
||||
#define CTS_BLOCK_SIZE 2
|
||||
#define CTS256_BLOCK_COUNT 16
|
||||
#define CTS512_BLOCK_COUNT 32
|
||||
|
||||
|
||||
// SR memory sizes
|
||||
@@ -757,17 +760,44 @@ static void print_st_general_info(uint8_t *data, uint8_t len) {
|
||||
PrintAndLogEx(SUCCESS, "Chip: %02X, " _YELLOW_("%s"), chipid, get_st_chip_model(chipid));
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
uint8_t product_code;
|
||||
const char *name;
|
||||
uint8_t block_count;
|
||||
} cts_product_info_t;
|
||||
|
||||
static const cts_product_info_t cts_products[] = {
|
||||
{ASK_CTS_PRODUCT_CODE_CTS256B, "CTS256B", CTS256_BLOCK_COUNT},
|
||||
{ASK_CTS_PRODUCT_CODE_CTS512B, "CTS512B", CTS512_BLOCK_COUNT},
|
||||
};
|
||||
|
||||
static const cts_product_info_t cts_unknown_product = {
|
||||
0x00,
|
||||
"unknown",
|
||||
CTS256_BLOCK_COUNT,
|
||||
};
|
||||
|
||||
static const cts_product_info_t *get_cts_product_info(uint8_t product_code) {
|
||||
for (size_t i = 0; i < ARRAYLEN(cts_products); i++) {
|
||||
if (cts_products[i].product_code == product_code) {
|
||||
return &cts_products[i];
|
||||
}
|
||||
}
|
||||
return &cts_unknown_product;
|
||||
}
|
||||
|
||||
// print UID info from ASK CT chips
|
||||
static void print_ct_general_info(void *vcard) {
|
||||
iso14b_cts_card_select_t card;
|
||||
memcpy(&card, (iso14b_cts_card_select_t *)vcard, sizeof(iso14b_cts_card_select_t));
|
||||
|
||||
uint32_t uid32 = MemLeToUint4byte(card.uid);
|
||||
const cts_product_info_t *product_info = get_cts_product_info(card.pc);
|
||||
PrintAndLogEx(NORMAL, "");
|
||||
PrintAndLogEx(SUCCESS, "ASK C-Ticket");
|
||||
PrintAndLogEx(SUCCESS, " UID: " _GREEN_("%s") " ( " _YELLOW_("%010u") " )", sprint_hex(card.uid, sizeof(card.uid)), uid32);
|
||||
PrintAndLogEx(SUCCESS, " Product Code: %02X", card.pc);
|
||||
PrintAndLogEx(SUCCESS, " Facility Code: %02X", card.fc);
|
||||
PrintAndLogEx(INFO, "--- " _CYAN_("ASK CTS / C-ticket") " -----------------------");
|
||||
PrintAndLogEx(SUCCESS, " UID : " _GREEN_("%s") " ( " _YELLOW_("%010u") " )", sprint_hex(card.uid, sizeof(card.uid)), uid32);
|
||||
PrintAndLogEx(SUCCESS, " Product Code : %02X ( " _YELLOW_("%s") " )", card.pc, product_info->name);
|
||||
PrintAndLogEx(SUCCESS, " Fab Code : %02X", card.fc);
|
||||
PrintAndLogEx(NORMAL, "");
|
||||
}
|
||||
|
||||
@@ -807,26 +837,26 @@ static void print_footer(void) {
|
||||
PrintAndLogEx(NORMAL, "");
|
||||
}
|
||||
|
||||
/*
|
||||
static void print_ct_blocks(uint8_t *data, size_t len) {
|
||||
|
||||
size_t blocks = len / ST25TB_SR_BLOCK_SIZE;
|
||||
size_t blocks = len / CTS_BLOCK_SIZE;
|
||||
|
||||
print_hdr();
|
||||
PrintAndLogEx(NORMAL, "");
|
||||
PrintAndLogEx(INFO, " block# | data | ascii");
|
||||
PrintAndLogEx(INFO, "---------+-------+------");
|
||||
|
||||
for (int i = 0; i <= blocks; i++) {
|
||||
for (int i = 0; i < blocks; i++) {
|
||||
PrintAndLogEx(INFO,
|
||||
"%3d/0x%02X | %s | %s | %s",
|
||||
"%3d/0x%02X | %s| %s",
|
||||
i,
|
||||
i,
|
||||
sprint_hex(data + (i * 4), 4),
|
||||
" ",
|
||||
sprint_ascii(data + (i * 4), 4)
|
||||
sprint_hex(data + (i * CTS_BLOCK_SIZE), CTS_BLOCK_SIZE),
|
||||
sprint_ascii(data + (i * CTS_BLOCK_SIZE), CTS_BLOCK_SIZE)
|
||||
);
|
||||
}
|
||||
print_footer();
|
||||
PrintAndLogEx(INFO, "---------+-------+------");
|
||||
PrintAndLogEx(NORMAL, "");
|
||||
}
|
||||
*/
|
||||
|
||||
static void print_sr_blocks(uint8_t *data, size_t len, const uint8_t *uid, bool dense_output) {
|
||||
|
||||
@@ -1446,6 +1476,85 @@ static int CmdHF14Binfo(const char *Cmd) {
|
||||
// #define ISO14443B_READ_BLK 0x08
|
||||
// #define ISO14443B_WRITE_BLK 0x09
|
||||
|
||||
static int select_cts_card_14443b(bool disconnect, iso14b_cts_card_select_t *card, bool verbose) {
|
||||
iso14b_raw_cmd_t packet = {
|
||||
.flags = (ISO14B_CONNECT | ISO14B_SELECT_CTS | ISO14B_CLEARTRACE),
|
||||
.timeout = 0,
|
||||
.rawlen = 0,
|
||||
};
|
||||
|
||||
if (disconnect) {
|
||||
packet.flags |= ISO14B_DISCONNECT;
|
||||
}
|
||||
|
||||
clearCommandBuffer();
|
||||
PacketResponseNG resp;
|
||||
SendCommandNG(CMD_HF_ISO14443B_COMMAND, (uint8_t *)&packet, sizeof(iso14b_raw_cmd_t));
|
||||
if (WaitForResponseTimeout(CMD_HF_ISO14443B_COMMAND, &resp, TIMEOUT) == false) {
|
||||
if (verbose) {
|
||||
PrintAndLogEx(WARNING, "timeout while waiting for reply");
|
||||
}
|
||||
return PM3_ETIMEOUT;
|
||||
}
|
||||
|
||||
if (resp.status != PM3_SUCCESS) {
|
||||
if (verbose) {
|
||||
PrintAndLogEx(FAILED, "ISO 14443-b CTS select failed");
|
||||
}
|
||||
return resp.status;
|
||||
}
|
||||
|
||||
if (resp.length < sizeof(iso14b_cts_card_select_t)) {
|
||||
if (verbose) {
|
||||
PrintAndLogEx(FAILED, "ISO 14443-3 CTS wrong length");
|
||||
}
|
||||
return PM3_ELENGTH;
|
||||
}
|
||||
|
||||
if (card) {
|
||||
memcpy(card, resp.data.asBytes, sizeof(iso14b_cts_card_select_t));
|
||||
}
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
static int read_cts_block(uint8_t blockno, uint8_t *out, uint16_t out_len) {
|
||||
uint8_t psize = sizeof(iso14b_raw_cmd_t) + 1;
|
||||
iso14b_raw_cmd_t *packet = (iso14b_raw_cmd_t *)calloc(1, psize);
|
||||
if (packet == NULL) {
|
||||
PrintAndLogEx(WARNING, "Failed to allocate memory");
|
||||
return PM3_EMALLOC;
|
||||
}
|
||||
packet->flags = (ISO14B_APPEND_CRC | ISO14B_RAW);
|
||||
packet->timeout = 0;
|
||||
packet->rawlen = 1;
|
||||
packet->raw[0] = ASK_READ | (blockno & ASK_BLOCK_ADDRESS_MASK);
|
||||
|
||||
clearCommandBuffer();
|
||||
PacketResponseNG resp;
|
||||
SendCommandNG(CMD_HF_ISO14443B_COMMAND, (uint8_t *)packet, psize);
|
||||
free(packet);
|
||||
if (WaitForResponseTimeout(CMD_HF_ISO14443B_COMMAND, &resp, TIMEOUT) == false) {
|
||||
return PM3_ETIMEOUT;
|
||||
}
|
||||
|
||||
if (resp.status != PM3_SUCCESS) {
|
||||
return resp.status;
|
||||
}
|
||||
|
||||
if (resp.length != CTS_BLOCK_SIZE + 2) {
|
||||
return PM3_ELENGTH;
|
||||
}
|
||||
|
||||
if (check_crc(CRC_14443_B, resp.data.asBytes, resp.length) == false) {
|
||||
return PM3_ECRC;
|
||||
}
|
||||
|
||||
if (out) {
|
||||
memcpy(out, resp.data.asBytes, MIN(out_len, CTS_BLOCK_SIZE));
|
||||
}
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
static int read_sr_block(uint8_t blockno, uint8_t *out, uint16_t out_len) {
|
||||
struct {
|
||||
uint8_t blockno;
|
||||
@@ -1614,6 +1723,7 @@ static bool HF14B_ask_ct_reader(bool verbose) {
|
||||
switch (resp.status) {
|
||||
case PM3_SUCCESS: {
|
||||
print_ct_general_info(resp.data.asBytes);
|
||||
PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf 14b ct") "`");
|
||||
return true;
|
||||
}
|
||||
case PM3_ELENGTH: {
|
||||
@@ -1686,6 +1796,7 @@ static bool HF14B_prime_reader(bool verbose) {
|
||||
return false;
|
||||
}
|
||||
print_prime_general_info(&card);
|
||||
PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf calypso") "`");
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1775,6 +1886,119 @@ static int CmdHF14BReader(const char *Cmd) {
|
||||
return readHF14B(cm, verbose, read_plot);
|
||||
}
|
||||
|
||||
static int CmdHF14BCtsDump(const char *Cmd) {
|
||||
CLIParserContext *ctx;
|
||||
CLIParserInit(&ctx, "hf 14b ctdump",
|
||||
"Read and print ASK CTS / C-ticket memory blocks",
|
||||
"hf 14b ctdump\n"
|
||||
"hf 14b ctdump -v"
|
||||
);
|
||||
|
||||
void *argtable[] = {
|
||||
arg_param_begin,
|
||||
arg_lit0("v", "verbose", "verbose output"),
|
||||
arg_param_end
|
||||
};
|
||||
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
||||
bool verbose = arg_get_lit(ctx, 1);
|
||||
CLIParserFree(ctx);
|
||||
|
||||
iso14b_cts_card_select_t card = {0};
|
||||
int status = select_cts_card_14443b(false, &card, verbose);
|
||||
if (status != PM3_SUCCESS) {
|
||||
switch_off_field_14b();
|
||||
PrintAndLogEx(WARNING, "no ASK CTS / C-ticket tag found");
|
||||
return status;
|
||||
}
|
||||
|
||||
print_ct_general_info(&card);
|
||||
|
||||
const cts_product_info_t *product_info = get_cts_product_info(card.pc);
|
||||
uint8_t block_count = product_info->block_count;
|
||||
uint8_t data[CTS512_BLOCK_COUNT * CTS_BLOCK_SIZE] = {0};
|
||||
|
||||
PrintAndLogEx(INFO, "Reading %u blocks", block_count);
|
||||
|
||||
for (uint8_t blockno = 0; blockno < block_count; blockno++) {
|
||||
status = PM3_ESOFT;
|
||||
for (uint8_t retry = 0; retry < 3; retry++) {
|
||||
status = read_cts_block(blockno, data + (blockno * CTS_BLOCK_SIZE), CTS_BLOCK_SIZE);
|
||||
if (status == PM3_SUCCESS) {
|
||||
break;
|
||||
}
|
||||
if (verbose) {
|
||||
PrintAndLogEx(DEBUG, "block %u read failed, status %d, retry %u", blockno, status, retry + 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (status != PM3_SUCCESS) {
|
||||
switch_off_field_14b();
|
||||
PrintAndLogEx(FAILED, "failed to read block %u (status %d)", blockno, status);
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
switch_off_field_14b();
|
||||
print_ct_blocks(data, block_count * CTS_BLOCK_SIZE);
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
static int CmdHF14BCtRdBl(const char *Cmd) {
|
||||
CLIParserContext *ctx;
|
||||
CLIParserInit(&ctx, "hf 14b ctrdbl",
|
||||
"Read an ASK CTS / C-ticket block",
|
||||
"hf 14b ctrdbl -b 6\n"
|
||||
"hf 14b ctrdbl --block 15 -v"
|
||||
);
|
||||
|
||||
void *argtable[] = {
|
||||
arg_param_begin,
|
||||
arg_int1("b", "block", "<dec>", "block number"),
|
||||
arg_lit0("v", "verbose", "verbose output"),
|
||||
arg_param_end
|
||||
};
|
||||
CLIExecWithReturn(ctx, Cmd, argtable, false);
|
||||
int blockno = arg_get_int_def(ctx, 1, -1);
|
||||
bool verbose = arg_get_lit(ctx, 2);
|
||||
CLIParserFree(ctx);
|
||||
|
||||
if (blockno < 0 || blockno > ASK_BLOCK_ADDRESS_MASK) {
|
||||
PrintAndLogEx(FAILED, "block number out of range, max %u (0x%02X), got " _RED_("%d"),
|
||||
ASK_BLOCK_ADDRESS_MASK, ASK_BLOCK_ADDRESS_MASK, blockno);
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
|
||||
iso14b_cts_card_select_t card = {0};
|
||||
int status = select_cts_card_14443b(false, &card, verbose);
|
||||
if (status != PM3_SUCCESS) {
|
||||
switch_off_field_14b();
|
||||
PrintAndLogEx(WARNING, "no ASK CTS / C-ticket tag found");
|
||||
return status;
|
||||
}
|
||||
|
||||
const cts_product_info_t *product_info = get_cts_product_info(card.pc);
|
||||
if (blockno >= product_info->block_count) {
|
||||
switch_off_field_14b();
|
||||
PrintAndLogEx(FAILED, "block number out of range for %s, max %u (0x%02X), got " _RED_("%d"),
|
||||
product_info->name, product_info->block_count - 1, product_info->block_count - 1, blockno);
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
|
||||
if (verbose) {
|
||||
print_ct_general_info(&card);
|
||||
}
|
||||
|
||||
uint8_t out[CTS_BLOCK_SIZE] = {0};
|
||||
status = read_cts_block((uint8_t)blockno, out, sizeof(out));
|
||||
switch_off_field_14b();
|
||||
|
||||
if (status == PM3_SUCCESS) {
|
||||
PrintAndLogEx(SUCCESS, "block %02u... " _GREEN_("%s") " | " _GREEN_("%s"),
|
||||
blockno, sprint_hex(out, sizeof(out)), sprint_ascii(out, sizeof(out)));
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
// Read SRI512|SRIX4K block
|
||||
static int CmdHF14BSriRdBl(const char *Cmd) {
|
||||
|
||||
@@ -3363,6 +3587,9 @@ static command_t CommandTable[] = {
|
||||
{"tearoff", CmdHF14BSriTearoff, IfPm3Iso14443b, "Tear-off attack on ST25TB/SRx counter blocks"},
|
||||
{"view", CmdHF14BView, AlwaysAvailable, "Display content from tag dump file"},
|
||||
{"valid", CmdSRIX4kValid, AlwaysAvailable, "SRIX4 checksum test"},
|
||||
{"---------", CmdHelp, IfPm3Iso14443b, "------------------ " _CYAN_("ASK CTS / C-ticket") " ------------------"},
|
||||
{"ctdump", CmdHF14BCtsDump, IfPm3Iso14443b, "Dump ASK CTS/C-ticket"},
|
||||
{"ctrdbl", CmdHF14BCtRdBl, IfPm3Iso14443b, "Read ASK CTS/C-ticket block"},
|
||||
{"---------", CmdHelp, IfPm3Iso14443b, "------------------------- " _CYAN_("Magic") " -----------------------"},
|
||||
{"setuid", CmdHF14BSetUID, IfPm3Iso14443b, "Set UID for magic card"},
|
||||
{NULL, NULL, NULL, NULL}
|
||||
|
||||
+7
-4
@@ -414,10 +414,13 @@ ISO 7816-4 Basic interindustry commands. For command APDU's.
|
||||
#define ASK_REQT 0x10
|
||||
#define ASK_IDENTIFY 0x0F
|
||||
#define ASK_SELECT 0x9F
|
||||
#define ASK_MULTREAD (0x1 << 4) // High nibble
|
||||
#define ASK_UPDATE (0x3 << 4) // High nibble
|
||||
#define ASK_WRITE (0x5 << 4) // High nibble
|
||||
#define ASK_READ (0x6 << 4) // High nibble
|
||||
#define ASK_CTS_PRODUCT_CODE_CTS256B 0x50
|
||||
#define ASK_CTS_PRODUCT_CODE_CTS512B 0x60
|
||||
#define ASK_BLOCK_ADDRESS_MASK 0x1F
|
||||
#define ASK_MULTREAD (0x1 << 5) // 001 AAAAA
|
||||
#define ASK_UPDATE (0x3 << 5) // 011 AAAAA
|
||||
#define ASK_WRITE (0x5 << 5) // 101 AAAAA
|
||||
#define ASK_READ (0x6 << 5) // 110 AAAAA
|
||||
#define ASK_DESACTIVATE 0xF0
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user