From 070211be1bc07c73d225ca6b03b1a974bcab8671 Mon Sep 17 00:00:00 2001 From: "Peter S. Hollander" Date: Tue, 17 Mar 2026 12:54:13 +0000 Subject: [PATCH 1/5] Add `--timeout` to `hf 15 sim` --- CHANGELOG.md | 1 + client/src/cmdhf15.c | 11 ++++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cad05b438..388072ad6 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] +- Added `-t` / `--timeout` option for `hf 15 sim` (@recursivenomad) ## [Permafrost][2026-02-25] - Added standalone mode `HF_DOEGOX_AUTH0`: UL-C / UL-AES unlocker (@doegox) diff --git a/client/src/cmdhf15.c b/client/src/cmdhf15.c index 1bbdfb951..a5b73b93f 100644 --- a/client/src/cmdhf15.c +++ b/client/src/cmdhf15.c @@ -1473,6 +1473,7 @@ static int CmdHF15Sim(const char *Cmd) { arg_param_begin, arg_str0("u", "uid", "", "UID, 8 hex bytes"), arg_int0("b", "blocksize", "", "block size (def 4)"), + arg_int0("t", "timeout", "", "timeout in ms (0 will run until button press - def 0)"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -1486,6 +1487,7 @@ static int CmdHF15Sim(const char *Cmd) { int uidlen = 0; CLIGetHexWithReturn(ctx, 1, payload.uid, &uidlen); payload.block_size = arg_get_int_def(ctx, 2, 4); + size_t timeout = arg_get_int_def(ctx, 3, 0); CLIParserFree(ctx); // sanity checks @@ -1494,6 +1496,10 @@ static int CmdHF15Sim(const char *Cmd) { return PM3_EINVARG; } + if (timeout == 0) { + timeout = -1; + } + PacketResponseNG resp; // get UID from emulator for printing @@ -1525,7 +1531,10 @@ static int CmdHF15Sim(const char *Cmd) { clearCommandBuffer(); SendCommandNG(CMD_HF_ISO15693_SIMULATE, (uint8_t *)&payload, sizeof(payload)); - WaitForResponse(CMD_HF_ISO15693_SIMULATE, &resp); + WaitForResponseTimeout(CMD_HF_ISO15693_SIMULATE, &resp, timeout); + if (timeout != -1) { + SendCommandNG(CMD_BREAK_LOOP, NULL, 0); + } PrintAndLogEx(INFO, "Done!"); return PM3_SUCCESS; } From bff9b9c37a1e12f29d8200619ef1dc29113dcc45 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Tue, 24 Mar 2026 17:33:09 +0700 Subject: [PATCH 2/5] added sanity check for too large length, and reworked the arrays in "hf 14b raw". It now uses 512b instead of 1024b ram :) Which is better for the stack --- armsrc/iso14443b.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/armsrc/iso14443b.c b/armsrc/iso14443b.c index 1c3d01f7c..4f61912df 100644 --- a/armsrc/iso14443b.c +++ b/armsrc/iso14443b.c @@ -1643,7 +1643,11 @@ void CodeAndTransmit14443bAsReader(const uint8_t *cmd, int len, uint32_t *start_ */ int iso14443b_apdu(uint8_t const *msg, size_t msg_len, bool send_chaining, void *rxdata, uint16_t rxmaxlen, uint8_t *response_byte, uint16_t *responselen) { - uint8_t real_cmd[msg_len + 4]; + if (msg_len > PM3_CMD_DATA_SIZE) { + return PM3_EINVARG; + } + + uint8_t real_cmd[PM3_CMD_DATA_SIZE + 4]; if (msg_len) { // ISO 14443 APDU frame: PCB [CID] [NAD] APDU CRC PCB=0x02 @@ -3096,8 +3100,10 @@ void SendRawCommand14443B(iso14b_raw_cmd_t *p) { set_tracing(true); - // receive buffer - uint8_t buf[PM3_CMD_DATA_SIZE] = {0x00}; + // receive buffer — sized for APDU response header + max payload, reused for all paths + uint8_t buf[sizeof(iso14b_raw_apdu_response_t) + PM3_CMD_DATA_SIZE]; + memset(buf, 0, sizeof(buf)); + iso14b_raw_apdu_response_t *payload = (iso14b_raw_apdu_response_t *)buf; int status = 0; uint32_t sendlen = sizeof(iso14b_card_select_t); @@ -3159,17 +3165,14 @@ void SendRawCommand14443B(iso14b_raw_cmd_t *p) { uint16_t responselen = 0; uint8_t response_byte = 0; bool chaining = ((p->flags & ISO14B_SEND_CHAINING) == ISO14B_SEND_CHAINING); - status = iso14443b_apdu(p->raw, p->rawlen, chaining, buf, sizeof(buf), &response_byte, &responselen); + status = iso14443b_apdu(p->raw, p->rawlen, chaining, payload->data, PM3_CMD_DATA_SIZE, &response_byte, &responselen); if (tearoff_hook() == PM3_ETEAROFF) { // tearoff occurred reply_ng(CMD_HF_ISO14443B_COMMAND, PM3_ETEAROFF, NULL, 0); } else { - uint8_t packet[responselen + 1 + 2]; - iso14b_raw_apdu_response_t *payload = (iso14b_raw_apdu_response_t *)packet; payload->response_byte = response_byte; payload->datalen = responselen; - memcpy(payload->data, buf, payload->datalen); - reply_ng(CMD_HF_ISO14443B_COMMAND, status, packet, sizeof(packet)); + reply_ng(CMD_HF_ISO14443B_COMMAND, status, buf, sizeof(iso14b_raw_apdu_response_t) + responselen); } } @@ -3198,7 +3201,7 @@ void SendRawCommand14443B(iso14b_raw_cmd_t *p) { eof_time += DELAY_ISO14443B_PCD_TO_PICC_READER; uint16_t retlen = 0; - status = Get14443bAnswerFromTag(buf, sizeof(buf), s_iso14b_timeout, &eof_time, &retlen); + status = Get14443bAnswerFromTag(buf, PM3_CMD_DATA_SIZE, s_iso14b_timeout, &eof_time, &retlen); if (status == PM3_SUCCESS) { sendlen = MIN(retlen, PM3_CMD_DATA_SIZE); reply_ng(CMD_HF_ISO14443B_COMMAND, status, Demod.output, sendlen); From 422b3b747344ac32e759e2e9b0975d34acc57399 Mon Sep 17 00:00:00 2001 From: "Peter S. Hollander" Date: Tue, 24 Mar 2026 10:53:13 +0000 Subject: [PATCH 3/5] Preserve default flow --- client/src/cmdhf15.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/client/src/cmdhf15.c b/client/src/cmdhf15.c index a5b73b93f..becd9f7fd 100644 --- a/client/src/cmdhf15.c +++ b/client/src/cmdhf15.c @@ -1473,7 +1473,7 @@ static int CmdHF15Sim(const char *Cmd) { arg_param_begin, arg_str0("u", "uid", "", "UID, 8 hex bytes"), arg_int0("b", "blocksize", "", "block size (def 4)"), - arg_int0("t", "timeout", "", "timeout in ms (0 will run until button press - def 0)"), + arg_int0("t", "timeout", "", "timeout in ms (def -1, ie. until button press)"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -1487,7 +1487,7 @@ static int CmdHF15Sim(const char *Cmd) { int uidlen = 0; CLIGetHexWithReturn(ctx, 1, payload.uid, &uidlen); payload.block_size = arg_get_int_def(ctx, 2, 4); - size_t timeout = arg_get_int_def(ctx, 3, 0); + size_t timeout = arg_get_int_def(ctx, 3, -1); CLIParserFree(ctx); // sanity checks @@ -1496,10 +1496,6 @@ static int CmdHF15Sim(const char *Cmd) { return PM3_EINVARG; } - if (timeout == 0) { - timeout = -1; - } - PacketResponseNG resp; // get UID from emulator for printing @@ -1532,7 +1528,7 @@ static int CmdHF15Sim(const char *Cmd) { clearCommandBuffer(); SendCommandNG(CMD_HF_ISO15693_SIMULATE, (uint8_t *)&payload, sizeof(payload)); WaitForResponseTimeout(CMD_HF_ISO15693_SIMULATE, &resp, timeout); - if (timeout != -1) { + if (timeout != (size_t) - 1) { SendCommandNG(CMD_BREAK_LOOP, NULL, 0); } PrintAndLogEx(INFO, "Done!"); From 2ccfa0638671d179a6274fcbc46f924021916267 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Tue, 24 Mar 2026 19:02:04 +0700 Subject: [PATCH 4/5] added sanity checks --- armsrc/mifareutil.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/armsrc/mifareutil.c b/armsrc/mifareutil.c index e3bf38d73..a9386a917 100644 --- a/armsrc/mifareutil.c +++ b/armsrc/mifareutil.c @@ -86,7 +86,11 @@ uint8_t mf_crypto1_encrypt4bit(struct Crypto1State *pcs, uint8_t data) { // send X byte basic commands uint16_t mifare_sendcmd(uint8_t cmd, uint8_t *data, uint8_t data_size, uint8_t *answer, uint16_t answer_len, uint8_t *answer_parity, uint32_t *timing) { - uint8_t dcmd[data_size + 3]; + if (data_size > 32) { + return 0; + } + + uint8_t dcmd[32 + 3]; dcmd[0] = cmd; if (data_size > 0) { memcpy(dcmd + 1, data, data_size); @@ -99,8 +103,9 @@ uint16_t mifare_sendcmd(uint8_t cmd, uint8_t *data, uint8_t data_size, uint8_t * } uint16_t len = ReaderReceive(answer, answer_len, answer_parity); if (len == 0) { - if (g_dbglevel >= DBG_ERROR) + if (g_dbglevel >= DBG_ERROR) { Dbprintf("%02X Cmd failed. Card timeout.", cmd); + } len = ReaderReceive(answer, answer_len, answer_parity); } return len; @@ -109,7 +114,11 @@ uint16_t mifare_sendcmd(uint8_t cmd, uint8_t *data, uint8_t data_size, uint8_t * // send X byte basic commands secure channel (UL AES) uint16_t mifare_sendcmd_schann(uint8_t *data, uint8_t data_size, uint8_t *answer, uint16_t answer_len, uint8_t *answer_parity, uint32_t *timing) { - uint8_t dcmd[data_size + 2]; + if (data_size > 16) { + return 0; + } + + uint8_t dcmd[16 + 2]; memset(dcmd, 0, sizeof(dcmd)); if (data_size > 0) { @@ -149,6 +158,7 @@ uint16_t mifare_sendcmd_short(struct Crypto1State *pcs, uint8_t crypted, uint8_t } else { ReaderTransmit(dcmd, sizeof(dcmd), timing); } + if (tearoff_hook() == PM3_ETEAROFF) { // tearoff occurred return 0; } @@ -160,6 +170,7 @@ uint16_t mifare_sendcmd_short(struct Crypto1State *pcs, uint8_t crypted, uint8_t } if (pcs && (crypted == CRYPT_ALL)) { + if (len == 1) { uint16_t res = 0; res |= (crypto1_bit(pcs, 0, 0) ^ BIT(answer[0], 0)) << 0; @@ -167,7 +178,9 @@ uint16_t mifare_sendcmd_short(struct Crypto1State *pcs, uint8_t crypted, uint8_t res |= (crypto1_bit(pcs, 0, 0) ^ BIT(answer[0], 2)) << 2; res |= (crypto1_bit(pcs, 0, 0) ^ BIT(answer[0], 3)) << 3; answer[0] = res; + } else { + for (pos = 0; pos < len; pos++) { answer[pos] = crypto1_byte(pcs, 0x00, 0) ^ answer[pos]; } From d1a6ef47040dc0ae23d0e2add2040f96ffe7b2bd Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Tue, 24 Mar 2026 19:03:13 +0700 Subject: [PATCH 5/5] guard , if someone wanted to break pm3 simulation.... which is very like not to happen --- armsrc/seos.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/armsrc/seos.c b/armsrc/seos.c index 520cf6d65..f56f632df 100644 --- a/armsrc/seos.c +++ b/armsrc/seos.c @@ -795,6 +795,11 @@ void SimulateSeos(seos_emulate_req_t *msg) { } uint8_t cmac_size = recvd_cmac_length; + if (cmac_size > 16) { + Dbprintf(_RED_("Get Data failed") ": CMAC size invalid."); + break; + } + if (!generate_cmac(diver_cmac_key, mac_input, mac_input_idx, cmac, msg->encr_alg)) { Dbprintf(_RED_("Get Data failed") ": Failed to create reply CMAC."); break;