From c268844d50031deab01d63ead9c4dc7a4327ff6f Mon Sep 17 00:00:00 2001 From: Davi Mikael <31720422+penegui@users.noreply.github.com> Date: Tue, 24 Mar 2026 20:22:53 -0300 Subject: [PATCH 1/7] Fix EMV flow for cards without usable READ RECORD Capture Track 2 (tag 57) from the GPO response when available and avoid sending the fixed READ RECORD APDU if the token was already found. This prevents unnecessary 6A83 responses and improves compatibility with cards that return the required data during GPO. --- armsrc/Standalone/hf_emvpng.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/armsrc/Standalone/hf_emvpng.c b/armsrc/Standalone/hf_emvpng.c index 21c5a593c..d603fdf24 100644 --- a/armsrc/Standalone/hf_emvpng.c +++ b/armsrc/Standalone/hf_emvpng.c @@ -301,9 +301,14 @@ void RunMod(void) { if (iso14443a_select_card(NULL, &card_a_info, NULL, true, 0, false)) { DbpString(_YELLOW_("+") "Found ISO 14443 Type A!"); - + + chktoken = false; for (uint8_t i = 0; i < 4; i++) { - chktoken = false; + + if (i == 3 && chktoken) { + break; // já achei a 57 no GPO, não preciso de READ RECORD + } + LED_C_OFF(); LED_B_ON(); uint8_t apdulen = iso14_apdu(apdus[i], (uint16_t) apduslen[i], false, apdubuffer, sizeof(apdubuffer), NULL); @@ -331,13 +336,13 @@ void RunMod(void) { } - } else if (i == 3) { + } else if (i == 2 || i == 3) { - // find track 2 - if (apdubuffer[u] == 0x57 && apdubuffer[u + 1] == 0x13 && !chktoken) { + // find track 2 in GPO or READ RECORD + if (u + 20 < apdulen && apdubuffer[u] == 0x57 && apdubuffer[u + 1] == 0x13 && !chktoken) { chktoken = true; - memcpy(&token, &apdubuffer[u + 2], 19); + memcpy(token, &apdubuffer[u + 2], 19); break; } } From e7c1d17b900c5192446f3212279bb1b4c83f9017 Mon Sep 17 00:00:00 2001 From: Davi Mikael <31720422+penegui@users.noreply.github.com> Date: Wed, 25 Mar 2026 00:23:34 -0300 Subject: [PATCH 2/7] hf_emvpng: translate inline comment to English Update the inline comment describing the GPO/READ RECORD flow to English, as requested in review. --- armsrc/Standalone/hf_emvpng.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/armsrc/Standalone/hf_emvpng.c b/armsrc/Standalone/hf_emvpng.c index d603fdf24..7de09f461 100644 --- a/armsrc/Standalone/hf_emvpng.c +++ b/armsrc/Standalone/hf_emvpng.c @@ -306,7 +306,7 @@ void RunMod(void) { for (uint8_t i = 0; i < 4; i++) { if (i == 3 && chktoken) { - break; // já achei a 57 no GPO, não preciso de READ RECORD + break; // Tag 57 was already found in GPO, no need to issue READ RECORD } LED_C_OFF(); From b12559a21c8a1217584aebe32328669de3900a88 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Wed, 25 Mar 2026 12:38:50 +0700 Subject: [PATCH 3/7] catch first scenario when auth_table_len == AUTH_TABLE_LEN --- armsrc/hitag2.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/armsrc/hitag2.c b/armsrc/hitag2.c index 9ba2bcaf9..f64620108 100644 --- a/armsrc/hitag2.c +++ b/armsrc/hitag2.c @@ -286,7 +286,7 @@ static void hitag2_handle_reader_command(uint8_t *rx, const size_t rxlen, uint8_ // Received RWD authentication challenge and response case 64: { // Store the authentication attempt - if (auth_table_len < (AUTH_TABLE_LENGTH - 8)) { + if (auth_table_len + 8 <= AUTH_TABLE_LENGTH) { memcpy(auth_table + auth_table_len, rx, 8); auth_table_len += 8; } @@ -1371,7 +1371,7 @@ void SniffHitag2(bool ledcontrol) { // Check if we recognize a valid authentication attempt if (rxlen == 64) { // Store the authentication attempt - if (auth_table_len < (AUTH_TABLE_LENGTH - 8)) { + if (auth_table_len + 8 <= AUTH_TABLE_LENGTH) { memcpy(auth_table + auth_table_len, rx, 8); auth_table_len += 8; } From 922bf5b4b3513e9d5891dfa14e7a8df6b1773e23 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Wed, 25 Mar 2026 12:49:43 +0700 Subject: [PATCH 4/7] added a sanity check --- armsrc/mifaredesfire.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/armsrc/mifaredesfire.c b/armsrc/mifaredesfire.c index 2ccc12fb2..b5c751876 100644 --- a/armsrc/mifaredesfire.c +++ b/armsrc/mifaredesfire.c @@ -323,6 +323,10 @@ void MifareDES_Auth1(uint8_t *datain) { memcpy(keybytes, PICC_MASTER_KEY24, 24); } } else { + if (payload->keylen > sizeof(keybytes)) { + OnErrorNG(CMD_HF_DESFIRE_AUTH1, PM3_EINVARG); + return; + } memcpy(keybytes, payload->key, payload->keylen); } From 572dcc4347a78f97fea706d1e8dd6a45e299db51 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Wed, 25 Mar 2026 13:00:05 +0700 Subject: [PATCH 5/7] some sanity checks --- armsrc/iso14443a.c | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index 5f522aed1..76dc70ed1 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -4492,16 +4492,21 @@ void DetectNACKbug(void) { set_tracing(false); } -/* /// -Based upon the SimulateIso14443aTag, this aims to instead take an AID Value you've supplied, and return your selected response. -It can also continue after the AID has been selected, and respond to other request types. -This was forked from the original function to allow for more flexibility in the future, and to increase the processing speed of the original function. -/// */ +// Increased the buffer size to allow for more complex responses +#define DYNAMIC_RESPONSE_BUFFER2_SIZE ( 512 ) +#define DYNAMIC_MODULATION_BUFFER2_SIZE ( 1536 ) + +// EvilDaemond +// Based upon the SimulateIso14443aTag, this aims to instead take an AID Value you've supplied, and return your selected response. +// It can also continue after the AID has been selected, and respond to other request types. +// This was forked from the original function to allow for more flexibility in the future, and to increase the processing speed of the original function. +// void SimulateIso14443aTagAID(uint8_t tagType, uint16_t flags, uint8_t *uid, uint8_t *ats, size_t ats_len, uint8_t *aid, size_t aid_len, uint8_t *selectaid_response, size_t selectaid_response_len, uint8_t *getdata_response, size_t getdata_response_len) { + tag_response_info_t *responses; uint32_t cuid = 0; uint8_t pages = 0; @@ -4514,14 +4519,19 @@ void SimulateIso14443aTagAID(uint8_t tagType, uint16_t flags, uint8_t *uid, // Copy the AID, AID Response, and the GetData APDU response into our variables if ((aid == NULL) || (selectaid_response == NULL) || (getdata_response == NULL)) { reply_ng(CMD_HF_MIFARE_SIMULATE, PM3_EINVARG, NULL, 0); + return; } // free eventually allocated BigBuf memory but keep Emulator Memory BigBuf_free_keep_EM(); - // Increased the buffer size to allow for more complex responses -#define DYNAMIC_RESPONSE_BUFFER2_SIZE 512 -#define DYNAMIC_MODULATION_BUFFER2_SIZE 1536 + // Response payloads must fit within the dynamic response buffer, + // accounting for 1-byte IBlock header + 1-byte optional CID (offset 0 or 1) + if (selectaid_response_len + 2 > DYNAMIC_RESPONSE_BUFFER2_SIZE || + getdata_response_len + 2 > DYNAMIC_RESPONSE_BUFFER2_SIZE) { + reply_ng(CMD_HF_MIFARE_SIMULATE, PM3_EINVARG, NULL, 0); + return; + } uint8_t *dynamic_response_buffer2 = BigBuf_calloc(DYNAMIC_RESPONSE_BUFFER2_SIZE); if (dynamic_response_buffer2 == NULL) { From 4be1967ab040636482b69f85d3f81c223cb8e7b8 Mon Sep 17 00:00:00 2001 From: team-orangeBlue <63470411+team-orangeBlue@users.noreply.github.com> Date: Wed, 25 Mar 2026 10:12:56 +0300 Subject: [PATCH 6/7] Add WTX support in ExchangeRaw14A For whatever reason there was no support for WTX replies from a tag. If a tag replied with an APDU frame of 0xF[n] instead of 0x0[n], the client would error out when it should not have done that. There's now support for that. Signed-off-by: team-orangeBlue <63470411+team-orangeBlue@users.noreply.github.com> --- client/src/cmdhf14a.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/client/src/cmdhf14a.c b/client/src/cmdhf14a.c index 44ef40c3a..dc3066bdd 100644 --- a/client/src/cmdhf14a.c +++ b/client/src/cmdhf14a.c @@ -1162,6 +1162,7 @@ int ExchangeRAW14a(uint8_t *datain, int datainlen, bool activateField, bool leav uint8_t *recv; PacketResponseNG resp; +resend: if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { recv = resp.data.asBytes; int iLen = resp.oldarg[0]; @@ -1184,7 +1185,23 @@ int ExchangeRAW14a(uint8_t *datain, int datainlen, bool activateField, bool leav } return PM3_ELENGTH; } - + if (recv[0] & 0xf0) { + uint8_t dbg_curr = DBG_NONE; + if (getDeviceDebugLevel(&dbg_curr) != PM3_SUCCESS) { + return PM3_EFAILED; + } + if (dbg_curr >= DBG_DEBUG) { + PrintAndLogEx(WARNING, "Card requested WTX"); + } + // WARNING for users of this function: + // This NO_DISCONNECT flag will NOT save you. If you run your commands with LeaveSignalOn set to false, they will still fail if a card asks for WTX. + // Better keep the flag on and do a DropField(); at the end. + SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_RAW | ISO14A_NO_DISCONNECT | cmdc, 5, 0, recv, min); // 2b frame +1b "data" + 2b crc. Hardcode length to 5 and reply to response + goto resend; + } + // OBS + // This if check below breaks command flow if the card for whatever reason decides to request a WTX. + // Because WTX will do a |= 0xF0 to the framing, this check triggers and everything that was happening will stop. if (recv[0] != data[0]) { if (silentMode == false) { PrintAndLogEx(ERR, "iso14443-4 framing error. Card send %2x must be %2x", recv[0], data[0]); From 54e9a48e7b4a16d00b1a9888f487d801d552f6d5 Mon Sep 17 00:00:00 2001 From: team-orangeBlue <63470411+team-orangeBlue@users.noreply.github.com> Date: Wed, 25 Mar 2026 10:15:30 +0300 Subject: [PATCH 7/7] Update CHANGELOG.md Signed-off-by: team-orangeBlue <63470411+team-orangeBlue@users.noreply.github.com> --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5a20f8bf4..040420bfe 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] +- Fixed WTX response behavior in `ExchangeRaw14A()` (@team-orangeBlue) - Added `-t` / `--timeout` option for `hf 15 sim` (@recursivenomad) - Added `--aid` parameter to `hf seos` commands (@kormax) - Added `hf iclass blacktears` command to perform an automated tearoff of block 1 to set non-secure page mode(@antiklesys)