fix breaking builds
[RRG-proxmark3.git] / armsrc / Standalone / hf_cardhopper.c
blobed6e660bf708660885cd793b68f2a3c08b1b2c03
1 /*
2 * hf_cardhopper standalone mode by Sam Haskins
4 * A component of our (me + Trevor Stevado) research on long-range relay
5 * attacks against 14a-based protocols (as presented at DEF CON '31).
6 * Works with a CardHopper (recommended) or BlueShark add-on.
8 * If you're reading this, you're clearly a very interesting person---
9 * do reach out if you get any fun results? [ sam AT loudmouth DOT io ]
10 * Good luck, and may the odds be ever in your favour!
12 * The companion Android app is available on our gitlab: gitlab.com/loudmouth-security
14 * For more information, see: https://media.defcon.org/DEF%20CON%2031/DEF%20CON%2031%20presentations/Trevor%20Stevado%20Sam%20Haskins%20-%20Unlocking%20Doors%20from%20Half%20a%20Continent%20Away.pdf
17 #include <string.h>
19 #include "appmain.h"
20 #include "BigBuf.h"
21 #include "dbprint.h"
22 #include "fpgaloader.h"
23 #include "iso14443a.h"
24 #include "protocols.h"
25 #include "proxmark3_arm.h"
26 #include "standalone.h"
27 #include "ticks.h"
28 #include "util.h"
29 #include "usart.h"
30 #include "cmd.h"
31 #include "usb_cdc.h"
33 #ifdef CARDHOPPER_USB
34 #define cardhopper_write usb_write
35 #define cardhopper_read usb_read_ng
36 bool cardhopper_data_available(void);
37 bool cardhopper_data_available(void) {
38 return usb_read_ng_has_buffered_data() || usb_poll_validate_length();
40 #else
41 #define cardhopper_write usart_writebuffer_sync
42 #define cardhopper_read usart_read_ng
43 #define cardhopper_data_available usart_rxdata_available
44 #endif
46 void ModInfo(void) {
47 DbpString(" HF - Long-range relay 14a over serial<->IP - a.k.a. CardHopper (Sam Haskins)");
51 typedef struct PACKED {
52 uint8_t len;
53 uint8_t dat[255];
54 } packet_t;
56 // Magic numbers
57 static const uint8_t magicREAD[4] = "READ";
58 static const uint8_t magicCARD[4] = "CARD";
59 static const uint8_t magicEND [4] = "\xff" "END";
60 static const uint8_t magicRSRT[7] = "RESTART";
61 static const uint8_t magicERR [4] = "\xff" "ERR";
62 static uint8_t magicACK [1] = "\xfe"; // is constant, but must be passed to API that doesn't like that
64 // Forward declarations
65 static void become_reader(void);
66 static void select_card(void);
68 static void become_card(void);
69 static void prepare_emulation(uint8_t *, uint16_t *, uint8_t *, packet_t *);
70 static void cook_ats(packet_t *, uint8_t, uint8_t);
71 static bool try_use_canned_response(const uint8_t *, int, tag_response_info_t *);
72 static void reply_with_packet(packet_t *);
74 static void read_packet(packet_t *);
75 static void write_packet(packet_t *);
77 static bool GetIso14443aCommandFromReaderInterruptible(uint8_t *, uint16_t, uint8_t *, int *);
80 void RunMod(void) {
81 // Ensure debug logs don't polute stream
82 #ifdef CARDHOPPER_USB
83 g_reply_via_usb = false;
84 g_reply_via_fpc = true;
85 #else
86 g_reply_via_usb = true;
87 g_reply_via_fpc = false;
88 #endif
90 StandAloneMode();
91 DbpString(_CYAN_("[@]") " CardHopper has started - waiting for mode");
92 FpgaDownloadAndGo(FPGA_BITSTREAM_HF);
94 clear_trace();
95 set_tracing(true);
97 // Indicate we are alive and in CardHopper
98 LEDsoff();
99 LED_A_ON();
100 LED_D_ON();
102 while (1) {
103 WDT_HIT();
105 packet_t modeRx = { 0 };
106 read_packet(&modeRx);
108 if (BUTTON_PRESS()) {
109 DbpString(_CYAN_("[@]") " Button pressed - Breaking from mode loop");
110 break;
113 if (modeRx.len == 0) {
114 DbpString(_CYAN_("[@]") " Zero length message");
115 continue;
118 if (modeRx.len == sizeof(magicREAD) && memcmp(magicREAD, modeRx.dat, sizeof(magicREAD)) == 0) {
119 DbpString(_CYAN_("[@]") " I am a READER. I talk to a CARD.");
120 become_reader();
121 } else if (modeRx.len == sizeof(magicCARD) && memcmp(magicCARD, modeRx.dat, sizeof(magicCARD)) == 0) {
122 DbpString(_CYAN_("[@]") " I am a CARD. I talk to a READER.");
123 become_card();
124 } else if (modeRx.len == sizeof(magicEND) && memcmp(magicEND, modeRx.dat, sizeof(magicEND)) == 0) {
125 break;
126 } else if (modeRx.len == sizeof(magicRSRT) && memcmp(magicRSRT, modeRx.dat, sizeof(magicRSRT)) == 0) {
127 DbpString(_CYAN_("[@]") " Got RESET but already reset.");
128 continue;
129 } else {
130 DbpString(_YELLOW_("[!]") " unknown mode!");
131 Dbhexdump(modeRx.len, modeRx.dat, true);
135 DbpString(_CYAN_("[@]") " exiting ...");
136 FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
137 LEDsoff();
141 static void become_reader(void) {
142 iso14443a_setup(FPGA_HF_ISO14443A_READER_MOD);
143 select_card(); // also sends UID, ATS
145 DbpString(_CYAN_("[@]") " entering reader main loop ...");
146 packet_t packet = { 0 };
147 packet_t *rx = &packet;
148 packet_t *tx = &packet;
149 uint8_t toCard[256] = { 0 };
150 uint8_t parity[MAX_PARITY_SIZE] = { 0 };
152 while (1) {
153 WDT_HIT();
155 read_packet(rx);
156 if (rx->len == sizeof(magicRSRT) && memcmp(magicRSRT, rx->dat, sizeof(magicRSRT)) == 0) break;
158 if (BUTTON_PRESS()) {
159 DbpString(_CYAN_("[@]") " Button pressed - Breaking from reader loop");
160 break;
163 if (rx->dat[0] == ISO14443A_CMD_RATS && rx->len == 4) {
164 // got RATS from reader, reset the card
165 FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
166 SpinDelay(40);
167 iso14443a_setup(FPGA_HF_ISO14443A_READER_MOD);
169 // re-select the card without RATS to allow replaying the real RATS
170 int ret = iso14443a_select_card(NULL, NULL, NULL, true, 0, true);
171 if (ret && ret != 1) {
172 Dbprintf(_RED_("[!]") " Error selecting card: %d", ret);
173 continue;
177 memcpy(toCard, rx->dat, rx->len);
178 AddCrc14A(toCard, rx->len);
179 ReaderTransmit(toCard, rx->len + 2, NULL);
181 tx->len = ReaderReceive(tx->dat, sizeof(tx->dat), parity);
182 if (tx->len == 0) {
183 tx->len = sizeof(magicERR);
184 memcpy(tx->dat, magicERR, sizeof(magicERR));
185 } else tx->len -= 2; // cut off the CRC
187 write_packet(tx);
192 static void select_card(void) {
193 iso14a_card_select_t card = { 0 };
194 while (1) {
195 WDT_HIT();
197 int ret = iso14443a_select_card(NULL, &card, NULL, true, 0, false);
198 if (ret && ret != 1)
199 Dbprintf(_RED_("[!]") " Error selecting card: %d", ret);
200 if (ret == 1) break;
202 SpinDelay(20);
205 DbpString(_CYAN_("[@]") " UID:");
206 Dbhexdump(card.uidlen, card.uid, false);
207 DbpString(_CYAN_("[@]") " ATS:");
208 Dbhexdump(card.ats_len - 2 /* no CRC */, card.ats, false);
210 packet_t tx = { 0 };
211 tx.len = card.uidlen;
212 memcpy(tx.dat, card.uid, tx.len);
213 write_packet(&tx);
215 tx.len = card.ats_len - 2;
216 memcpy(tx.dat, card.ats, tx.len);
217 write_packet(&tx);
221 static void become_card(void) {
222 iso14443a_setup(FPGA_HF_ISO14443A_TAGSIM_LISTEN);
224 uint8_t tagType;
225 uint16_t flags = 0;
226 uint8_t data[PM3_CMD_DATA_SIZE] = { 0 };
227 packet_t ats = { 0 };
228 prepare_emulation(&tagType, &flags, data, &ats);
230 tag_response_info_t *canned;
231 uint32_t cuid;
232 uint32_t counters[3] = { 0 };
233 uint8_t tearings[3] = { 0xbd, 0xbd, 0xbd };
234 uint8_t pages;
235 SimulateIso14443aInit(tagType, flags, data, NULL, 0, &canned, &cuid, counters, tearings, &pages);
237 DbpString(_CYAN_("[@]") " Setup done - entering emulation loop");
238 int fromReaderLen;
239 uint8_t fromReaderDat[256] = { 0 };
240 uint8_t parity[MAX_PARITY_SIZE] = { 0 };
241 packet_t packet = { 0 };
242 packet_t *tx = &packet;
243 packet_t *rx = &packet;
245 while (1) {
246 WDT_HIT();
248 if (!GetIso14443aCommandFromReaderInterruptible(fromReaderDat, sizeof(fromReaderDat), parity, &fromReaderLen)) {
249 if (cardhopper_data_available()) {
250 read_packet(rx);
251 if (memcmp(magicRSRT, rx->dat, sizeof(magicRSRT)) == 0) {
252 DbpString(_CYAN_("[@]") " Breaking from emulation loop");
253 break;
255 } else if (BUTTON_PRESS()) {
256 DbpString(_CYAN_("[@]") " Button pressed - Breaking from emulation loop");
257 break;
259 continue;
262 // Option 1: Use a canned response
263 if (try_use_canned_response(fromReaderDat, fromReaderLen, canned)) continue;
265 // Option 2: Reply with our cooked ATS
266 bool no_reply = false;
267 if (fromReaderDat[0] == ISO14443A_CMD_RATS && fromReaderLen == 4) {
268 reply_with_packet(&ats);
270 // fallthrough to still send the RATS to the card so it can learn the CID
271 // but don't send the reply since we've already sent our cooked ATS
272 no_reply = true;
275 // Option 3: Relay the message
276 tx->len = fromReaderLen - 2; // cut off the crc
277 memcpy(tx->dat, fromReaderDat, tx->len);
278 write_packet(tx);
280 read_packet(rx);
281 if (!no_reply && rx->len > 0) {
282 reply_with_packet(rx);
288 static void prepare_emulation(uint8_t *tagType, uint16_t *flags, uint8_t *data, packet_t *ats) {
289 packet_t tagTypeRx = { 0 };
290 read_packet(&tagTypeRx);
291 packet_t timeModeRx = { 0 };
292 read_packet(&timeModeRx);
293 packet_t uidRx = { 0 };
294 read_packet(&uidRx);
295 read_packet(ats);
297 *tagType = tagTypeRx.dat[0];
298 Dbprintf(_CYAN_("[@]") " Using tag type: %hhu", *tagType);
300 DbpString(_CYAN_("[@]") " Time control parameters:");
301 Dbhexdump(timeModeRx.len, timeModeRx.dat, false);
302 uint8_t fwi = timeModeRx.dat[0] & 0x0f;
303 uint8_t sfgi = timeModeRx.dat[1] & 0x0f;
304 Dbprintf(_CYAN_("[@]") " Parsed as fwi = %hhu, sfgi = %hhu", fwi, sfgi);
306 if (fwi == 0xf) {
307 DbpString(_YELLOW_("[!]") " Refusing to use 15 as FWI - will use 14");
308 fwi = 0xe;
310 if (sfgi == 0xf) {
311 DbpString(_YELLOW_("[!]") " Refusing to use 15 as SFGI - will use 14");
312 sfgi = 0xe;
315 memcpy(data, uidRx.dat, uidRx.len);
316 FLAG_SET_UID_IN_DATA(*flags, uidRx.len);
317 DbpString(_CYAN_("[@]") " UID:");
318 Dbhexdump(uidRx.len, data, false);
319 Dbprintf(_CYAN_("[@]") " Flags: %hu", *flags);
321 DbpString(_CYAN_("[@]") " Original ATS:");
322 Dbhexdump(ats->len, ats->dat, false);
323 cook_ats(ats, fwi, sfgi);
324 DbpString(_CYAN_("[@]") " Cooked ATS:");
325 Dbhexdump(ats->len, ats->dat, false);
329 static void cook_ats(packet_t *ats, uint8_t fwi, uint8_t sfgi) {
330 if (ats->len != ats->dat[0]) {
331 DbpString(_RED_("[!]") " Malformed ATS - unable to cook; things may go wrong!");
332 return;
335 uint8_t t0 = 0x70; // TA, TB, and TC transmitted, FSCI nibble set later
336 uint8_t ta = 0x80; // only 106kbps rate supported, and must be same in both directions - PM3 doesn't support any other rates
337 uint8_t tb = (fwi << 4) | sfgi; // cooked value
338 uint8_t tc = 0;
340 uint8_t historical_len = 0;
341 uint8_t *historical_bytes;
342 if (ats->len > 1) {
343 // T0 byte exists when ats length > 1
345 uint8_t orig_t0 = ats->dat[1];
346 // Update FSCI in T0 from the received ATS
347 t0 |= orig_t0 & 0x0F;
349 uint8_t len = ats->len - 2;
350 uint8_t *orig_ats_ptr = &ats->dat[2];
351 if (orig_t0 & 0x10) {
352 // TA present
353 if (len < 1) {
354 DbpString(_RED_("[!]") " Malformed ATS - unable to cook; things may go wrong!");
355 return;
357 orig_ats_ptr++;
358 len--;
360 if (orig_t0 & 0x20) {
361 // TB present
362 if (len < 1) {
363 DbpString(_RED_("[!]") " Malformed ATS - unable to cook; things may go wrong!");
364 return;
366 orig_ats_ptr++;
367 len--;
369 if (orig_t0 & 0x40) {
370 // TC present, extract protocol parameters
371 if (len < 1) {
372 DbpString(_RED_("[!]") " Malformed ATS - unable to cook; things may go wrong!");
373 return;
375 tc = *orig_ats_ptr;
376 orig_ats_ptr++;
377 len--;
380 historical_bytes = orig_ats_ptr;
381 historical_len = len;
382 } else {
383 // T0 byte missing, update FSCI in T0 to the default value of 2
384 t0 |= 0x02;
387 packet_t cooked_ats = { 0 };
388 cooked_ats.len = 5 + historical_len;
389 cooked_ats.dat[0] = cooked_ats.len;
390 cooked_ats.dat[1] = t0;
391 cooked_ats.dat[2] = ta;
392 cooked_ats.dat[3] = tb;
393 cooked_ats.dat[4] = tc;
395 if (historical_len > 0) {
396 memcpy(cooked_ats.dat + 5, historical_bytes, historical_len);
399 memcpy(ats, &cooked_ats, sizeof(packet_t));
403 static bool try_use_canned_response(const uint8_t *dat, int len, tag_response_info_t *canned) {
404 if ((dat[0] == ISO14443A_CMD_REQA || dat[0] == ISO14443A_CMD_WUPA) && len == 1) {
405 EmSendPrecompiledCmd(canned + RESP_INDEX_ATQA);
406 return true;
409 if (dat[1] == 0x20 && len == 2) {
410 if (dat[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT) {
411 EmSendPrecompiledCmd(canned + RESP_INDEX_UIDC1);
412 return true;
413 } else if (dat[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT_2) {
414 EmSendPrecompiledCmd(canned + RESP_INDEX_UIDC2);
415 return true;
416 } else if (dat[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT_3) {
417 EmSendPrecompiledCmd(canned + RESP_INDEX_UIDC3);
418 return true;
422 if (dat[1] == 0x70 && len == 9) {
423 if (dat[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT) {
424 EmSendPrecompiledCmd(canned + RESP_INDEX_SAKC1);
425 return true;
426 } else if (dat[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT_2) {
427 EmSendPrecompiledCmd(canned + RESP_INDEX_SAKC2);
428 return true;
429 } else if (dat[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT_3) {
430 EmSendPrecompiledCmd(canned + RESP_INDEX_SAKC3);
431 return true;
435 // high nibble of PPS is PPS CMD, low nibble of PPS is CID
436 if ((dat[0] & 0xF0) == ISO14443A_CMD_PPS) {
437 EmSendPrecompiledCmd(canned + RESP_INDEX_PPS);
438 return true;
441 // No response is expected to these 14a commands
442 if ((dat[0] & 0xF7) == ISO14443A_CMD_WTX) return true; // bit 0x08 indicates CID following
443 if (dat[0] == ISO14443A_CMD_HALT && len == 4) return true;
445 // Ignore Apple ECP2 polling
446 if (dat[0] == ECP_HEADER) return true;
448 return false;
452 static uint8_t g_responseBuffer [512 ] = { 0 };
453 static uint8_t g_modulationBuffer[1024] = { 0 };
455 static void reply_with_packet(packet_t *packet) {
456 tag_response_info_t response = { 0 };
457 response.response = g_responseBuffer;
458 response.modulation = g_modulationBuffer;
460 memcpy(response.response, packet->dat, packet->len);
461 AddCrc14A(response.response, packet->len);
462 response.response_n = packet->len + 2;
464 prepare_tag_modulation(&response, sizeof(g_modulationBuffer));
465 EmSendPrecompiledCmd(&response);
469 static void read_packet(packet_t *packet) {
470 while (!cardhopper_data_available()) {
471 WDT_HIT();
472 SpinDelayUs(100);
473 if (BUTTON_PRESS()) {
474 DbpString(_CYAN_("[@]") " Button pressed while waiting for packet - aborting");
475 return;
479 cardhopper_read((uint8_t *) &packet->len, 1);
481 uint32_t dataReceived = 0;
482 while (dataReceived != packet->len) {
483 while (!cardhopper_data_available()) {
484 WDT_HIT();
485 if (BUTTON_PRESS()) {
486 DbpString(_CYAN_("[@]") " Button pressed while reading packet - aborting");
487 return;
491 dataReceived += cardhopper_read(packet->dat + dataReceived, packet->len - dataReceived);
493 if (packet->len == 0x50 && dataReceived >= sizeof(PacketResponseNGPreamble) && packet->dat[0] == 0x4D && packet->dat[1] == 0x33 && packet->dat[2] == 0x61) {
494 // PM3 NG packet magic
495 DbpString(_CYAN_("[@]") " PM3 NG packet received - ignoring");
497 // clear any remaining buffered data
498 while (cardhopper_data_available()) {
499 cardhopper_read(packet->dat, 255);
502 packet->len = 0;
503 return;
506 cardhopper_write(magicACK, sizeof(magicACK));
510 static void write_packet(packet_t *packet) {
511 cardhopper_write((uint8_t *) packet, packet->len + 1);
515 static bool GetIso14443aCommandFromReaderInterruptible(uint8_t *received, uint16_t received_max_len, uint8_t *par, int *len) {
516 LED_D_OFF();
517 FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_TAGSIM_LISTEN);
519 Uart14aInit(received, received_max_len, par);
521 uint8_t b = (uint8_t)AT91C_BASE_SSC->SSC_RHR;
522 (void)b;
524 uint8_t flip = 0;
525 uint16_t checker = 4000;
526 for (;;) {
527 WDT_HIT();
529 if (flip == 3) {
530 if (cardhopper_data_available() || BUTTON_PRESS())
531 return false;
533 flip = 0;
536 if (checker-- == 0) {
537 flip++;
538 checker = 4000;
541 if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) {
542 b = (uint8_t)AT91C_BASE_SSC->SSC_RHR;
543 if (MillerDecoding(b, 0)) {
544 *len = GetUart14a()->len;
545 return true;
549 return false;