fix to work with different SAMs. Fixed some overflow bugs and better asn1 decoding. Also enjoys the speed up from i2c sim module. Thanks Claude

This commit is contained in:
iceman1001
2026-08-27 01:59:33 +02:00
parent 84ad2fb8d4
commit 7488b5bb79
6 changed files with 151 additions and 38 deletions
+73 -9
View File
@@ -105,6 +105,60 @@ uint16_t sam_bd_offset(const uint8_t *response, uint16_t response_len) {
* that offset, clamped to what actually arrived, or 0 if the frame is too short
* to hold anything.
*/
// How many bytes of routing tail this SAM puts in front of the ASN.1 payload.
// A Grace SAM uses 6 where SAM_RX_ASN1_PREFIX_LENGTH says 5, so pick the one
// whose node length accounts for the frame exactly: tag, length, contents,
// SW1 SW2.
uint16_t sam_rx_prefix_len(const uint8_t *rx, uint16_t rx_len) {
uint16_t fallback = 0;
for (uint16_t ofs = SAM_RX_ASN1_PREFIX_LENGTH;
ofs <= (uint16_t)(SAM_RX_ASN1_PREFIX_LENGTH + 1);
ofs++) {
if ((uint16_t)(ofs + 1) >= rx_len) {
break;
}
if ((rx[ofs] != 0xa1) && (rx[ofs] != 0xbd)) {
continue;
}
if ((uint16_t)(ofs + 2 + rx[ofs + 1] + 2) == rx_len) {
return ofs;
}
if (fallback == 0) {
fallback = ofs;
}
}
return (fallback != 0) ? fallback : (uint16_t)SAM_RX_ASN1_PREFIX_LENGTH;
}
// The SAM asks for a card exchange with an a1 node holding an 80 <len> APDU.
// Older SAMs flagged it with 0x61 in the routing tail, which is where the
// fixed sam_rx_buf[1] test came from - a Grace SAM puts 0x14 there instead, so
// key off the ASN.1 node, which both generations agree on.
bool sam_relay_pending(const uint8_t *rx, uint16_t rx_len) {
uint16_t p = sam_rx_prefix_len(rx, rx_len);
if ((uint16_t)(p + 4) >= rx_len) {
return false;
}
return ((rx[p] == 0xa1) && (rx[p + 2] == 0xa1) && (rx[p + 4] == 0x80));
}
// The tag <-> SAM relay ends on an a1 02 82 00 node. The routing tail is 5 or
// 6 bytes depending on the SAM - the same reason sam_bd_offset() searches - so
// anchor on the node rather than indexing a fixed offset 7.
bool sam_relay_complete(const uint8_t *rx, uint16_t rx_len) {
uint16_t ofs = sam_rx_prefix_len(rx, rx_len);
if ((uint16_t)(ofs + 2) >= rx_len) {
return false;
}
return ((rx[ofs] == 0xa1) && (rx[ofs + 2] == 0x82));
}
uint16_t sam_response_payload(const uint8_t *rx, uint16_t rx_len, uint16_t *payload_len) {
uint16_t ofs = sam_bd_offset(rx, rx_len);
@@ -609,7 +663,7 @@ int sam_relay_iso15_loop(
// Nothing to relay - the SAM answered directly (final response already in
// sam_rx_buf). This is the normal case for SAM-internal commands.
if (sam_rx_buf[1] != 0x61) {
if (sam_relay_pending(sam_rx_buf, *sam_rx_len) == false) {
return PM3_SUCCESS;
}
@@ -624,11 +678,11 @@ int sam_relay_iso15_loop(
switch_clock_to_countsspclk();
// tag <-> SAM exchange starts here
while (sam_rx_buf[1] == 0x61) {
while (sam_relay_pending(sam_rx_buf, *sam_rx_len)) {
uint32_t start_time = GetCountSspClk();
uint32_t eof_time = start_time + DELAY_ICLASS_VICC_TO_VCD_READER;
nfc_tx_len = sam_copy_payload_sam2nfc(nfc_tx_buf, sam_rx_buf);
nfc_tx_len = sam_copy_payload_sam2nfc(nfc_tx_buf, sam_rx_buf, *sam_rx_len);
// PAGESEL (0x84) substitution for 2K PicoPass cards. A 2K card has a
// single book/page and does not answer PAGESEL, but the encode-side SAM
@@ -720,7 +774,7 @@ int sam_relay_iso15_loop(
// last SAM->TAG
// c1 61 c1 00 00 a1 02 >>82<< 00 90 00
if (sam_rx_buf[7] == 0x82) {
if (sam_relay_complete(sam_rx_buf, *sam_rx_len)) {
// tag <-> SAM exchange ends here
break;
}
@@ -738,7 +792,7 @@ int sam_relay_iso15_loop(
// interpreter command). That response is ALREADY in sam_rx_buf -
// sending the ack now would overwrite it with a bare 90 00.
// So only ack in case (a).
if (sam_rx_buf[1] == 0x61) {
if (sam_relay_pending(sam_rx_buf, *sam_rx_len)) {
static const uint8_t hfack[] = {
0xbd, 0x04, 0xa0, 0x02, 0x82, 0x00
};
@@ -763,7 +817,7 @@ int sam_relay_iso15_loop(
return PM3_SUCCESS;
}
uint16_t sam_copy_payload_sam2nfc(uint8_t *nfc_tx_buf, uint8_t *sam_rx_buf) {
uint16_t sam_copy_payload_sam2nfc(uint8_t *nfc_tx_buf, uint8_t *sam_rx_buf, uint16_t sam_rx_len) {
// SAM resp:
// c1 61 c1 00 00
// a1 10 <- nfc command
@@ -779,8 +833,18 @@ uint16_t sam_copy_payload_sam2nfc(uint8_t *nfc_tx_buf, uint8_t *sam_rx_buf) {
// NFC req:
// 0C 05 DE 64
// copy data out of c1->a1>->a1->80 node
uint16_t nfc_tx_len = (uint8_t) * (sam_rx_buf + 10);
memcpy(nfc_tx_buf, sam_rx_buf + 11, nfc_tx_len);
// copy data out of the a1->a1->80 node, which sits after a routing tail
// that is 5 bytes on some SAMs and 6 on others
uint16_t p = sam_rx_prefix_len(sam_rx_buf, sam_rx_len);
if ((uint16_t)(p + 5) >= sam_rx_len) {
return 0;
}
uint16_t nfc_tx_len = sam_rx_buf[p + 5];
if ((uint16_t)(p + 6 + nfc_tx_len) > sam_rx_len) {
return 0;
}
memcpy(nfc_tx_buf, sam_rx_buf + p + 6, nfc_tx_len);
return nfc_tx_len;
}
+5 -1
View File
@@ -26,6 +26,10 @@ static const uint8_t SAM_RX_ASN1_PREFIX_LENGTH = 5;
uint16_t sam_bd_offset(const uint8_t *response, uint16_t response_len);
// Offset of the response node plus how much of it to forward. See the .c file.
uint16_t sam_rx_prefix_len(const uint8_t *rx, uint16_t rx_len);
bool sam_relay_pending(const uint8_t *rx, uint16_t rx_len);
bool sam_relay_complete(const uint8_t *rx, uint16_t rx_len);
uint16_t sam_response_payload(const uint8_t *rx, uint16_t rx_len, uint16_t *payload_len);
int sam_rxtx(const uint8_t *data, uint16_t n, uint8_t *resp, uint16_t *resplen);
@@ -77,7 +81,7 @@ void sam_append_asn1_node(const uint8_t *root, const uint8_t *node, uint8_t type
void sam_send_ack(void);
uint16_t sam_copy_payload_nfc2sam(uint8_t *sam_tx, uint8_t *nfc_rx, uint8_t nfc_len);
uint16_t sam_copy_payload_sam2nfc(uint8_t *nfc_tx_buf, uint8_t *sam_rx_buf);
uint16_t sam_copy_payload_sam2nfc(uint8_t *nfc_tx_buf, uint8_t *sam_rx_buf, uint16_t sam_rx_len);
// NOTE: derived from sam_picopass.c::sam_send_request_iso15's relay loop.
int sam_relay_iso15_loop(
+8 -8
View File
@@ -94,15 +94,15 @@ static int sam_send_request_iso15(const uint8_t *const request, const uint8_t re
sam_rx_buf, &sam_rx_len
);
if (sam_rx_buf[1] == 0x61) { // commands to be relayed to card starts with 0x61
if (sam_relay_pending(sam_rx_buf, sam_rx_len)) { // commands to be relayed to card starts with 0x61
switch_clock_to_countsspclk();
// tag <-> SAM exchange starts here
while (sam_rx_buf[1] == 0x61) {
while (sam_relay_pending(sam_rx_buf, sam_rx_len)) {
uint32_t start_time = GetCountSspClk();
uint32_t eof_time = start_time + DELAY_ICLASS_VICC_TO_VCD_READER;
nfc_tx_len = sam_copy_payload_sam2nfc(nfc_tx_buf, sam_rx_buf);
nfc_tx_len = sam_copy_payload_sam2nfc(nfc_tx_buf, sam_rx_buf, sam_rx_len);
bool is_cmd_check = ((nfc_tx_buf[0] & 0x0F) == ICLASS_CMD_CHECK);
@@ -184,7 +184,7 @@ static int sam_send_request_iso15(const uint8_t *const request, const uint8_t re
// last SAM->TAG
// c1 61 c1 00 00 a1 02 >>82<< 00 90 00
if (sam_rx_buf[7] == 0x82) {
if (sam_relay_complete(sam_rx_buf, sam_rx_len)) {
// tag <-> SAM exchange ends here
break;
}
@@ -344,9 +344,9 @@ static int sam_send_request_emulated(const uint8_t *const request, const uint8_t
Dbprintf("Emulate: initial SAM resp[1]=%02x rx_len=%u", sam_rx_buf[1], sam_rx_len);
}
if (sam_rx_buf[1] == 0x61) {
while (sam_rx_buf[1] == 0x61) {
nfc_tx_len = sam_copy_payload_sam2nfc(nfc_tx_buf, sam_rx_buf);
if (sam_relay_pending(sam_rx_buf, sam_rx_len)) {
while (sam_relay_pending(sam_rx_buf, sam_rx_len)) {
nfc_tx_len = sam_copy_payload_sam2nfc(nfc_tx_buf, sam_rx_buf, sam_rx_len);
if (g_dbglevel >= DBG_INFO) {
Dbprintf("Emulate: SAM NFC cmd [%u]: %02x %02x ...", nfc_tx_len,
@@ -473,7 +473,7 @@ static int sam_send_request_emulated(const uint8_t *const request, const uint8_t
Dbprintf("Emulate: SAM rx[1]=%02x rx[7]=%02x", sam_rx_buf[1], sam_rx_buf[7]);
}
if (sam_rx_buf[7] == 0x82) {
if (sam_relay_complete(sam_rx_buf, sam_rx_len)) {
break;
}
}
+17 -6
View File
@@ -177,23 +177,34 @@ static int sam_send_request_iso14a(const uint8_t *const request, const uint8_t r
sam_rx_buf, &sam_rx_len
);
if (sam_rx_buf[1] == 0x61) { // commands to be relayed to card starts with 0x61
if (sam_relay_pending(sam_rx_buf, sam_rx_len)) { // commands to be relayed to card starts with 0x61
// tag <-> SAM exchange starts here
while (sam_rx_buf[1] == 0x61) {
while (sam_relay_pending(sam_rx_buf, sam_rx_len)) {
switch_clock_to_countsspclk();
nfc_tx_len = sam_copy_payload_sam2nfc(nfc_tx_buf, sam_rx_buf);
nfc_tx_len = sam_copy_payload_sam2nfc(nfc_tx_buf, sam_rx_buf, sam_rx_len);
nfc_rx_len = iso14_apdu(nfc_tx_buf, nfc_tx_len, false, nfc_rx_buf, ISO7816_MAX_FRAME, NULL);
// iceman: should check nfc_rx_len , if negative something went wrong...
int nfc_res = iso14_apdu(nfc_tx_buf, nfc_tx_len, false, nfc_rx_buf, ISO7816_MAX_FRAME, NULL);
switch_clock_to_ticks();
// A card that went away returns negative, which wrapped through
// uint16_t and was relayed on as a 253 byte answer.
if (nfc_res < 2) {
if (g_dbglevel >= DBG_ERROR) {
Dbprintf("SEOS relay: card exchange failed (%d)", nfc_res);
}
res = PM3_ECARDEXCHANGE;
goto out;
}
nfc_rx_len = (uint16_t)nfc_res;
sam_tx_len = sam_copy_payload_nfc2sam(sam_tx_buf, nfc_rx_buf, nfc_rx_len - 2);
sam_send_payload(0x14, 0x0a, 0x14, sam_tx_buf, &sam_tx_len, sam_rx_buf, &sam_rx_len);
// last SAM->TAG
// c1 61 c1 00 00 a1 02 >>82<< 00 90 00
if (sam_rx_buf[7] == 0x82) {
if (sam_relay_complete(sam_rx_buf, sam_rx_len)) {
// tag <-> SAM exchange ends here
break;
}
+24 -7
View File
@@ -8131,14 +8131,31 @@ static int CmdHFiClassSAMExtract(const char *Cmd) {
return res;
}
const uint8_t *oid = pacs + 2 + pacs_length;
const uint8_t oid_length = oid[1];
const uint8_t *oid_data = oid + 2;
PrintAndLogEx(SUCCESS, "SIO OID.......... " _GREEN_("%s"), sprint_hex_inrow(oid_data, oid_length));
// The a0 element holds 80 (PACS) and optionally 81 (SIO OID) and 82
// (media type). An iCLASS SE credential often omits 81, so walk the
// nodes rather than assuming all three are present in order.
const uint8_t *p = pacs + 2 + pacs_length;
const uint8_t *end = d + 6 + d[5];
if (end > d + resp.length) {
end = d + resp.length;
}
const uint8_t *mediaType = oid + 2 + oid_length;
const uint8_t mediaType_data = mediaType[2];
PrintAndLogEx(SUCCESS, "SIO Media Type... " _GREEN_("%s"), getSioMediaTypeInfo(mediaType_data));
while (p + 1 < end) {
uint8_t tag = p[0];
uint8_t len = p[1];
if (p + 2 + len > end) {
break;
}
if (tag == 0x81) {
PrintAndLogEx(SUCCESS, "SIO OID.......... " _GREEN_("%s"), sprint_hex_inrow(p + 2, len));
} else if ((tag == 0x82) && (len >= 1)) {
PrintAndLogEx(SUCCESS, "SIO Media Type... " _GREEN_("%s"), getSioMediaTypeInfo(p[2]));
}
p += 2 + len;
}
} else if (break_nrmac && d[0] == 0x05) {
PrintAndLogEx(SUCCESS, "Nr-MAC........... " _GREEN_("%s"), sprint_hex_inrow(d + 1, 8));
if (verbose) {
+24 -7
View File
@@ -2373,14 +2373,31 @@ static int CmdHfSeosSAM(const char *Cmd) {
return res;
}
const uint8_t *oid = pacs + 2 + pacs_length;
const uint8_t oid_length = oid[1];
const uint8_t *oid_data = oid + 2;
PrintAndLogEx(SUCCESS, "SIO OID.......: " _GREEN_("%s"), sprint_hex_inrow(oid_data, oid_length));
// The a0 element holds 80 (PACS) and optionally 81 (SIO OID) and 82
// (media type). An iCLASS SE credential often omits 81, so walk the
// nodes rather than assuming all three are present in order.
const uint8_t *p = pacs + 2 + pacs_length;
const uint8_t *end = d + 6 + d[5];
if (end > d + resp.length) {
end = d + resp.length;
}
const uint8_t *mediaType = oid + 2 + oid_length;
const uint8_t mediaType_data = mediaType[2];
PrintAndLogEx(SUCCESS, "SIO Media Type: " _GREEN_("%s"), getSioMediaTypeInfo(mediaType_data));
while (p + 1 < end) {
uint8_t tag = p[0];
uint8_t len = p[1];
if (p + 2 + len > end) {
break;
}
if (tag == 0x81) {
PrintAndLogEx(SUCCESS, "SIO OID.......: " _GREEN_("%s"), sprint_hex_inrow(p + 2, len));
} else if ((tag == 0x82) && (len >= 1)) {
PrintAndLogEx(SUCCESS, "SIO Media Type: " _GREEN_("%s"), getSioMediaTypeInfo(p[2]));
}
p += 2 + len;
}
} else {
print_hex(d, resp.length);