mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2025-08-19 21:03:48 -07:00
Multiple hf_cardhopper improvements
Allow button presses to break Handle non-zero CID from reader by relaying RATS to the card and improving PPS and WTX handling More reliably cook ATS values Ignore packets that look like they're coming from the client (NG packets)
This commit is contained in:
parent
b135413995
commit
1828358ab0
2 changed files with 126 additions and 28 deletions
|
@ -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...
|
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]
|
## [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)
|
- 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)
|
- Added `hf 15 wipe` - fills card memory with zeros (@iceman1001)
|
||||||
- Changed `hf xerox info` - now prints some part info (@iceman1001)
|
- Changed `hf xerox info` - now prints some part info (@iceman1001)
|
||||||
|
|
|
@ -78,7 +78,9 @@ void RunMod(void) {
|
||||||
// Ensure debug logs don't polute stream
|
// Ensure debug logs don't polute stream
|
||||||
#ifdef CARDHOPPER_USB
|
#ifdef CARDHOPPER_USB
|
||||||
g_reply_via_usb = false;
|
g_reply_via_usb = false;
|
||||||
|
g_reply_via_fpc = true;
|
||||||
#else
|
#else
|
||||||
|
g_reply_via_usb = true;
|
||||||
g_reply_via_fpc = false;
|
g_reply_via_fpc = false;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -100,6 +102,11 @@ void RunMod(void) {
|
||||||
packet_t modeRx = { 0 };
|
packet_t modeRx = { 0 };
|
||||||
read_packet(&modeRx);
|
read_packet(&modeRx);
|
||||||
|
|
||||||
|
if (BUTTON_PRESS()) {
|
||||||
|
DbpString(_CYAN_("[@]") " Button pressed - Breaking from mode loop");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (memcmp(magicREAD, modeRx.dat, sizeof(magicREAD)) == 0) {
|
if (memcmp(magicREAD, modeRx.dat, sizeof(magicREAD)) == 0) {
|
||||||
DbpString(_CYAN_("[@]") " I am a READER. I talk to a CARD.");
|
DbpString(_CYAN_("[@]") " I am a READER. I talk to a CARD.");
|
||||||
become_reader();
|
become_reader();
|
||||||
|
@ -137,6 +144,20 @@ static void become_reader(void) {
|
||||||
read_packet(rx);
|
read_packet(rx);
|
||||||
if (memcmp(magicRSRT, rx->dat, sizeof(magicRSRT)) == 0) break;
|
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);
|
memcpy(toCard, rx->dat, rx->len);
|
||||||
AddCrc14A(toCard, rx->len);
|
AddCrc14A(toCard, rx->len);
|
||||||
ReaderTransmit(toCard, rx->len + 2, NULL);
|
ReaderTransmit(toCard, rx->len + 2, NULL);
|
||||||
|
@ -212,9 +233,12 @@ static void become_card(void) {
|
||||||
if (cardhopper_data_available()) {
|
if (cardhopper_data_available()) {
|
||||||
read_packet(rx);
|
read_packet(rx);
|
||||||
if (memcmp(magicRSRT, rx->dat, sizeof(magicRSRT)) == 0) {
|
if (memcmp(magicRSRT, rx->dat, sizeof(magicRSRT)) == 0) {
|
||||||
DbpString(_CYAN_("[@]") " Breaking from reader loop");
|
DbpString(_CYAN_("[@]") " Breaking from emulation loop");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
} else if (BUTTON_PRESS()) {
|
||||||
|
DbpString(_CYAN_("[@]") " Button pressed - Breaking from emulation loop");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -223,9 +247,13 @@ static void become_card(void) {
|
||||||
if (try_use_canned_response(fromReaderDat, fromReaderLen, canned)) continue;
|
if (try_use_canned_response(fromReaderDat, fromReaderLen, canned)) continue;
|
||||||
|
|
||||||
// Option 2: Reply with our cooked ATS
|
// Option 2: Reply with our cooked ATS
|
||||||
|
bool no_reply = false;
|
||||||
if (fromReaderDat[0] == ISO14443A_CMD_RATS && fromReaderLen == 4) {
|
if (fromReaderDat[0] == ISO14443A_CMD_RATS && fromReaderLen == 4) {
|
||||||
reply_with_packet(&ats);
|
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
|
// Option 3: Relay the message
|
||||||
|
@ -234,8 +262,10 @@ static void become_card(void) {
|
||||||
write_packet(tx);
|
write_packet(tx);
|
||||||
|
|
||||||
read_packet(rx);
|
read_packet(rx);
|
||||||
|
if (!no_reply && rx->len > 0) {
|
||||||
reply_with_packet(rx);
|
reply_with_packet(rx);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -286,28 +316,71 @@ static void cook_ats(packet_t *ats, uint8_t fwi, uint8_t sfgi) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the ATS is too short (unusual), pad it to length with hopefully-sensible data
|
uint8_t t0 = 0x70; // TA, TB, and TC transmitted, FSCI nibble set later
|
||||||
// Might be better for the phone side to do this tbh
|
uint8_t ta = 0x80; // only 106kbps rate supported, and must be same in both directions - PM3 doesn't support any other rates
|
||||||
if (ats->len == 1) {
|
uint8_t tb = (fwi << 4) | sfgi; // cooked value
|
||||||
ats->len = 4;
|
uint8_t tc = 0;
|
||||||
ats->dat[0] = 0x04;
|
|
||||||
ats->dat[1] = 0x78;
|
uint8_t historical_len = 0;
|
||||||
ats->dat[2] = 0x77;
|
uint8_t *historical_bytes;
|
||||||
// ats->dat[3] = 0x80;
|
if (ats->len > 1) {
|
||||||
} else if (ats->len == 2) {
|
// T0 byte exists when ats length > 1
|
||||||
ats->len = 4;
|
|
||||||
ats->dat[0] = 0x04;
|
uint8_t orig_t0 = ats->dat[1];
|
||||||
ats->dat[2] = 0x77;
|
// Update FSCI in T0 from the received ATS
|
||||||
// ats->dat[3] = 0x80;
|
t0 |= orig_t0 & 0x0F;
|
||||||
} else if (ats->len == 3) {
|
|
||||||
ats->len = 4;
|
uint8_t len = ats->len - 2;
|
||||||
ats->dat[0] = 0x04;
|
uint8_t *orig_ats_ptr = &ats->dat[2];
|
||||||
// ats->dat[3] = 0x80;
|
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--;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the SFGI as well as the FWI - needed for some older readers (firmware revs?)
|
historical_bytes = orig_ats_ptr;
|
||||||
uint8_t cookedTB0 = (fwi << 4) | sfgi;
|
historical_len = len;
|
||||||
ats->dat[3] = cookedTB0;
|
} else {
|
||||||
|
// T0 byte missing, update FSCI in T0 to the default value of 2
|
||||||
|
t0 |= 0x02;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
EmSendPrecompiledCmd(canned + RESP_INDEX_PPS);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// No response is expected to these 14a commands
|
// 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;
|
if (dat[0] == ISO14443A_CMD_HALT && len == 4) return true;
|
||||||
|
|
||||||
// Ignore Apple ECP2 polling
|
// Ignore Apple ECP2 polling
|
||||||
if (dat[0] == 0x6a) return true;
|
if (dat[0] == ECP_HEADER) return true;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -380,15 +454,38 @@ static void read_packet(packet_t *packet) {
|
||||||
while (!cardhopper_data_available()) {
|
while (!cardhopper_data_available()) {
|
||||||
WDT_HIT();
|
WDT_HIT();
|
||||||
SpinDelayUs(100);
|
SpinDelayUs(100);
|
||||||
|
if (BUTTON_PRESS()) {
|
||||||
|
DbpString(_CYAN_("[@]") " Button pressed while waiting for packet - aborting");
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cardhopper_read((uint8_t *) &packet->len, 1);
|
cardhopper_read((uint8_t *) &packet->len, 1);
|
||||||
|
|
||||||
uint32_t dataReceived = 0;
|
uint32_t dataReceived = 0;
|
||||||
while (dataReceived != packet->len) {
|
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);
|
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));
|
cardhopper_write(magicACK, sizeof(magicACK));
|
||||||
}
|
}
|
||||||
|
@ -414,7 +511,7 @@ static bool GetIso14443aCommandFromReaderInterruptible(uint8_t *received, uint8_
|
||||||
WDT_HIT();
|
WDT_HIT();
|
||||||
|
|
||||||
if (flip == 3) {
|
if (flip == 3) {
|
||||||
if (cardhopper_data_available())
|
if (cardhopper_data_available() || BUTTON_PRESS())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
flip = 0;
|
flip = 0;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue