mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2025-08-21 22:03:42 -07:00
Merge pull request #2249 from nvx/cardhopper_improvements
Multiple hf_cardhopper improvements
This commit is contained in:
commit
5db2276602
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