mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2026-09-11 18:29:22 -07:00
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:
+73
-9
@@ -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
@@ -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(
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user