diff --git a/CHANGELOG.md b/CHANGELOG.md index 98496244f..a6fa13eb7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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] + - Changed `hf_cardhopper` - Allow button presses to break, handle non-zero CID from reader by relaying RATS and improving PPS and WTX handling, more reliably cook ATS, ignore client packets on serial line (@nvx) - Fixed `data diff` - client no longer crashes when using short widths on long filenames (@iceman1001) - Added `hf 15 wipe` - fills card memory with zeros (@iceman1001) - Changed `hf xerox info` - now prints some part info (@iceman1001) diff --git a/armsrc/Standalone/hf_cardhopper.c b/armsrc/Standalone/hf_cardhopper.c index b589542c4..2949ada78 100644 --- a/armsrc/Standalone/hf_cardhopper.c +++ b/armsrc/Standalone/hf_cardhopper.c @@ -78,7 +78,9 @@ void RunMod(void) { // Ensure debug logs don't polute stream #ifdef CARDHOPPER_USB g_reply_via_usb = false; + g_reply_via_fpc = true; #else + g_reply_via_usb = true; g_reply_via_fpc = false; #endif @@ -100,6 +102,11 @@ void RunMod(void) { packet_t modeRx = { 0 }; read_packet(&modeRx); + if (BUTTON_PRESS()) { + DbpString(_CYAN_("[@]") " Button pressed - Breaking from mode loop"); + break; + } + if (memcmp(magicREAD, modeRx.dat, sizeof(magicREAD)) == 0) { DbpString(_CYAN_("[@]") " I am a READER. I talk to a CARD."); become_reader(); @@ -137,6 +144,20 @@ static void become_reader(void) { read_packet(rx); if (memcmp(magicRSRT, rx->dat, sizeof(magicRSRT)) == 0) break; + if (rx->dat[0] == ISO14443A_CMD_RATS && rx->len == 4) { + // got RATS from reader, reset the card + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + SpinDelay(40); + iso14443a_setup(FPGA_HF_ISO14443A_READER_MOD); + + // re-select the card without RATS to allow replaying the real RATS + int ret = iso14443a_select_card(NULL, NULL, NULL, true, 0, true); + if (ret && ret != 1) { + Dbprintf(_RED_("[!]") " Error selecting card: %d", ret); + continue; + } + } + memcpy(toCard, rx->dat, rx->len); AddCrc14A(toCard, rx->len); ReaderTransmit(toCard, rx->len + 2, NULL); @@ -212,9 +233,12 @@ static void become_card(void) { if (cardhopper_data_available()) { read_packet(rx); if (memcmp(magicRSRT, rx->dat, sizeof(magicRSRT)) == 0) { - DbpString(_CYAN_("[@]") " Breaking from reader loop"); + DbpString(_CYAN_("[@]") " Breaking from emulation loop"); break; } + } else if (BUTTON_PRESS()) { + DbpString(_CYAN_("[@]") " Button pressed - Breaking from emulation loop"); + break; } continue; } @@ -223,9 +247,13 @@ static void become_card(void) { if (try_use_canned_response(fromReaderDat, fromReaderLen, canned)) continue; // Option 2: Reply with our cooked ATS + bool no_reply = false; if (fromReaderDat[0] == ISO14443A_CMD_RATS && fromReaderLen == 4) { reply_with_packet(&ats); - continue; + + // fallthrough to still send the RATS to the card so it can learn the CID + // but don't send the reply since we've already sent our cooked ATS + no_reply = true; } // Option 3: Relay the message @@ -234,7 +262,9 @@ static void become_card(void) { write_packet(tx); read_packet(rx); - reply_with_packet(rx); + if (!no_reply && rx->len > 0) { + reply_with_packet(rx); + } } } @@ -286,28 +316,71 @@ static void cook_ats(packet_t *ats, uint8_t fwi, uint8_t sfgi) { return; } - // If the ATS is too short (unusual), pad it to length with hopefully-sensible data - // Might be better for the phone side to do this tbh - if (ats->len == 1) { - ats->len = 4; - ats->dat[0] = 0x04; - ats->dat[1] = 0x78; - ats->dat[2] = 0x77; - // ats->dat[3] = 0x80; - } else if (ats->len == 2) { - ats->len = 4; - ats->dat[0] = 0x04; - ats->dat[2] = 0x77; - // ats->dat[3] = 0x80; - } else if (ats->len == 3) { - ats->len = 4; - ats->dat[0] = 0x04; - // ats->dat[3] = 0x80; + uint8_t t0 = 0x70; // TA, TB, and TC transmitted, FSCI nibble set later + uint8_t ta = 0x80; // only 106kbps rate supported, and must be same in both directions - PM3 doesn't support any other rates + uint8_t tb = (fwi << 4) | sfgi; // cooked value + uint8_t tc = 0; + + uint8_t historical_len = 0; + uint8_t *historical_bytes; + if (ats->len > 1) { + // T0 byte exists when ats length > 1 + + uint8_t orig_t0 = ats->dat[1]; + // Update FSCI in T0 from the received ATS + t0 |= orig_t0 & 0x0F; + + uint8_t len = ats->len - 2; + uint8_t *orig_ats_ptr = &ats->dat[2]; + if (orig_t0 & 0x10) { + // TA present + if (len < 1) { + DbpString(_RED_("[!]") " Malformed ATS - unable to cook; things may go wrong!"); + return; + } + orig_ats_ptr++; + len--; + } + if (orig_t0 & 0x20) { + // TB present + if (len < 1) { + DbpString(_RED_("[!]") " Malformed ATS - unable to cook; things may go wrong!"); + return; + } + orig_ats_ptr++; + len--; + } + if (orig_t0 & 0x40) { + // TC present, extract protocol parameters + if (len < 1) { + DbpString(_RED_("[!]") " Malformed ATS - unable to cook; things may go wrong!"); + return; + } + tc = *orig_ats_ptr; + orig_ats_ptr++; + len--; + } + + historical_bytes = orig_ats_ptr; + historical_len = len; + } else { + // T0 byte missing, update FSCI in T0 to the default value of 2 + t0 |= 0x02; } - // Set the SFGI as well as the FWI - needed for some older readers (firmware revs?) - uint8_t cookedTB0 = (fwi << 4) | sfgi; - ats->dat[3] = cookedTB0; + packet_t cooked_ats = { 0 }; + cooked_ats.len = 5+historical_len; + cooked_ats.dat[0] = cooked_ats.len; + cooked_ats.dat[1] = t0; + cooked_ats.dat[2] = ta; + cooked_ats.dat[3] = tb; + cooked_ats.dat[4] = tc; + + if (historical_len > 0) { + memcpy(cooked_ats.dat+5, historical_bytes, historical_len); + } + + memcpy(ats, &cooked_ats, sizeof(packet_t)); } @@ -343,17 +416,18 @@ static bool try_use_canned_response(const uint8_t *dat, int len, tag_response_in } } - if (dat[0] == ISO14443A_CMD_PPS) { + // high nibble of PPS is PPS CMD, low nibble of PPS is CID + if ((dat[0] & 0xF0) == ISO14443A_CMD_PPS) { EmSendPrecompiledCmd(canned + RESP_INDEX_PPS); return true; } // No response is expected to these 14a commands - if ((dat[0] == 0xf2 && len == 4) || dat[0] == 0xfa) return true; + if ((dat[0] & 0xF7) == ISO14443A_CMD_WTX) return true; // bit 0x08 indicates CID following if (dat[0] == ISO14443A_CMD_HALT && len == 4) return true; // Ignore Apple ECP2 polling - if (dat[0] == 0x6a) return true; + if (dat[0] == ECP_HEADER) return true; return false; } @@ -380,15 +454,38 @@ static void read_packet(packet_t *packet) { while (!cardhopper_data_available()) { WDT_HIT(); SpinDelayUs(100); + if (BUTTON_PRESS()) { + DbpString(_CYAN_("[@]") " Button pressed while waiting for packet - aborting"); + return; + } } cardhopper_read((uint8_t *) &packet->len, 1); uint32_t dataReceived = 0; while (dataReceived != packet->len) { - while (!cardhopper_data_available()) WDT_HIT(); + while (!cardhopper_data_available()) { + WDT_HIT(); + if (BUTTON_PRESS()) { + DbpString(_CYAN_("[@]") " Button pressed while reading packet - aborting"); + return; + } + } dataReceived += cardhopper_read(packet->dat + dataReceived, packet->len - dataReceived); + + if (packet->len == 0x50 && dataReceived >= sizeof(PacketResponseNGPreamble) && packet->dat[0] == 0x4D && packet->dat[1] == 0x33 && packet->dat[2] == 0x61) { + // PM3 NG packet magic + DbpString(_CYAN_("[@]") " PM3 NG packet recieved - ignoring"); + + // clear any remaining buffered data + while(cardhopper_data_available()) { + cardhopper_read(packet->dat, 255); + } + + packet->len = 0; + return; + } } cardhopper_write(magicACK, sizeof(magicACK)); } @@ -414,7 +511,7 @@ static bool GetIso14443aCommandFromReaderInterruptible(uint8_t *received, uint8_ WDT_HIT(); if (flip == 3) { - if (cardhopper_data_available()) + if (cardhopper_data_available() || BUTTON_PRESS()) return false; flip = 0;