From d4796d8d1026ed0e2f8c129d53a865ed7410ef6c Mon Sep 17 00:00:00 2001 From: team-orangeBlue <63470411+team-orangeBlue@users.noreply.github.com> Date: Fri, 29 May 2026 12:15:54 +0300 Subject: [PATCH 01/11] Increase delay for ExchangeRaw14A This function is primarily used by Mifare Plus auths. One problem that is present is when using keys 9002 or 9003 to upgrade a card to a new security level, the card will likely spend more time than usual adjusting all settings before it replies, as such causing a timeout. This increase fixes that. Signed-off-by: team-orangeBlue <63470411+team-orangeBlue@users.noreply.github.com> --- client/src/cmdhf14a.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/cmdhf14a.c b/client/src/cmdhf14a.c index 712cfd6a4..6df7e0136 100644 --- a/client/src/cmdhf14a.c +++ b/client/src/cmdhf14a.c @@ -1163,7 +1163,7 @@ int ExchangeRAW14a(uint8_t *datain, int datainlen, bool activateField, bool leav uint8_t *recv; PacketResponseNG resp; resend: - if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { + if (WaitForResponseTimeout(CMD_ACK, &resp, 7000)) { recv = resp.data.asBytes; int iLen = resp.oldarg[0]; From 1b19a9e3e89a1d4148d157fd856c875f9c99c0f4 Mon Sep 17 00:00:00 2001 From: team-orangeBlue <63470411+team-orangeBlue@users.noreply.github.com> Date: Fri, 29 May 2026 12:17:02 +0300 Subject: [PATCH 02/11] Implement nonfirst Add support for non-first mifare plus auth Signed-off-by: team-orangeBlue <63470411+team-orangeBlue@users.noreply.github.com> --- client/src/cmdhfmf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/cmdhfmf.c b/client/src/cmdhfmf.c index e4b971149..3065a4285 100644 --- a/client/src/cmdhfmf.c +++ b/client/src/cmdhfmf.c @@ -6833,7 +6833,7 @@ static int CmdHF14AMfAuth4(const char *Cmd) { return PM3_ESOFT; } - return MifareAuth4(NULL, keyn, key, true, false, true, true, false); + return MifareAuth4(NULL, keyn, key, false, true, false, true, true, false); } // https://www.nxp.com/docs/en/application-note/AN10787.pdf From b9becbc817d93f4561c92fd23919b945b47814f0 Mon Sep 17 00:00:00 2001 From: team-orangeBlue <63470411+team-orangeBlue@users.noreply.github.com> Date: Fri, 29 May 2026 12:32:30 +0300 Subject: [PATCH 03/11] Add non-first auth support Overhaul of the auth function Now a new bool can be passed if the auth coming in is meant to be a followup. This avoids regeneration of Ti. Maybe that's faster. Not functional for any management key. Signed-off-by: team-orangeBlue <63470411+team-orangeBlue@users.noreply.github.com> --- client/src/mifare/mifare4.c | 125 +++++++++++++++++++++++++----------- 1 file changed, 88 insertions(+), 37 deletions(-) diff --git a/client/src/mifare/mifare4.c b/client/src/mifare/mifare4.c index 8ad0dbccb..b2065af6c 100644 --- a/client/src/mifare/mifare4.c +++ b/client/src/mifare/mifare4.c @@ -204,23 +204,28 @@ int CalculateMAC(mf4Session_t *mf4session, MACType_t mtype, uint8_t blockNum, ui return aes_cmac8(NULL, mf4session->Kmac, macdata, mac, macdatalen); } -int MifareAuth4(mf4Session_t *mf4session, const uint8_t *keyn, uint8_t *key, bool activateField, bool leaveSignalON, bool dropFieldIfError, bool verbose, bool silentMode) { +int MifareAuth4(mf4Session_t *mf4session, const uint8_t *keyn, uint8_t *key, bool nonfirst, bool activateField, bool leaveSignalON, bool dropFieldIfError, bool verbose, bool silentMode) { uint8_t data[257] = {0}; int datalen = 0; - - uint8_t RndA[17] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00}; + if (nonfirst && memcmp(mf4session->Kmac, data, 16) == 0) { // compiler abuse + PrintAndLogEx(WARNING, "Function invocation error: cannot do non-first authentication yet"); + PrintAndLogEx(HINT, "Try to do first authentication"); + return PM3_EINVARG; + } // While TI maybe could be rolled as a zero, MACing key absolutely won't be. Don't tell me it'll happen, it won't, the chances are effectively zero. + uint8_t RndA[17] = {0x50, 0x4D, 0x33, 0x20, 0x52, 0x52, 0x47, 0x20, 0x32, 0x30, 0x32, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00}; uint8_t RndB[17] = {0}; if (silentMode) { verbose = false; } - if (mf4session) { + if (mf4session && !nonfirst) { mf4session->Authenticated = false; } - uint8_t cmd1[] = {0x70, keyn[1], keyn[0], 0x00}; - int res = ExchangeRAW14a(cmd1, sizeof(cmd1), activateField, true, data, sizeof(data), &datalen, silentMode); + uint8_t cmd1[4]; + if (nonfirst) { cmd1[0] = 0x76; cmd1[1] = keyn[1]; cmd1[2] = keyn[0]; } else { cmd1[0] = 0x70; cmd1[1] = keyn[1]; cmd1[2] = keyn[0]; cmd1[3] = 0x00; } + int res = ExchangeRAW14a(cmd1, nonfirst ? 3 : 4, activateField, true, data, sizeof(data), &datalen, silentMode); if (res != PM3_SUCCESS) { if (silentMode == false) { @@ -234,6 +239,7 @@ int MifareAuth4(mf4Session_t *mf4session, const uint8_t *keyn, uint8_t *key, boo } if (verbose) { + PrintAndLogEx(INFO, ">phase1: %s", sprint_hex(cmd1, nonfirst ? 3 : 4)); PrintAndLogEx(INFO, "< phase1: %s", sprint_hex(data, datalen)); } @@ -259,7 +265,7 @@ int MifareAuth4(mf4Session_t *mf4session, const uint8_t *keyn, uint8_t *key, boo return PM3_EWRONGANSWER; } - if (datalen != 19) { // code 1b + 16b + crc 2b + if (datalen != 19) { // code 1b + Rnd 16b + crc 2b if (silentMode == false) { PrintAndLogEx(ERR, "Card response must be 19 bytes long instead of: %d", datalen); } @@ -270,22 +276,65 @@ int MifareAuth4(mf4Session_t *mf4session, const uint8_t *keyn, uint8_t *key, boo return PM3_EWRONGANSWER; } - aes_decode(NULL, key, &data[1], RndB, 16); - RndB[16] = RndB[0]; - if (verbose) { - PrintAndLogEx(INFO, "RndB: %s", sprint_hex(RndB, 16)); - } - uint8_t cmd2[33] = {0}; cmd2[0] = 0x72; uint8_t raw[32] = {0}; - memmove(raw, RndA, 16); - memmove(&raw[16], &RndB[1], 16); + uint8_t IVR[16]; + uint8_t IVW[16]; + // Non-first auth applies all the wild sorcery from the encryption magic (replies have "read IVs", commands must have "write IVs"). + // To save instructions I'm going to just do one big if check + if (nonfirst) { + // WARNING TO IMPLEMENTERS + // This code is in theory NOT accurate to the confidential datasheet for Mifare Plus. + // Refer to proper IV generation in commit f29c94954f0d4958ba7947d11a44f61c900d4168. + memcpy(&IVR[0], &mf4session->R_Ctr, 2); + memcpy(&IVR[2], &mf4session->W_Ctr, 2); + memcpy(&IVR[4], &mf4session->R_Ctr, 2); + memcpy(&IVR[6], &mf4session->W_Ctr, 2); + memcpy(&IVR[8], &mf4session->R_Ctr, 2); + memcpy(&IVR[10], &mf4session->W_Ctr, 2); + memcpy(&IVR[12], &mf4session->R_Ctr, 2); + memcpy(&IVR[14], &mf4session->W_Ctr, 2); + memcpy(&IVR[12], mf4session->TI, 4); - aes_encode(NULL, key, raw, &cmd2[1], 32); - if (verbose) { - PrintAndLogEx(INFO, ">phase2: %s", sprint_hex(cmd2, 33)); + memcpy(&IVW[0], &mf4session->R_Ctr, 2); + memcpy(&IVW[2], &mf4session->W_Ctr, 2); + memcpy(&IVW[4], &mf4session->R_Ctr, 2); + memcpy(&IVW[6], &mf4session->W_Ctr, 2); + memcpy(&IVW[8], &mf4session->R_Ctr, 2); + memcpy(&IVW[10], &mf4session->W_Ctr, 2); + memcpy(&IVW[12], &mf4session->R_Ctr, 2); + memcpy(&IVW[14], &mf4session->W_Ctr, 2); + memcpy(IVW, mf4session->TI, 4); + + aes_decode(IVR, key, &data[1], RndB, 16); + RndB[16] = RndB[0]; + if (verbose) { + PrintAndLogEx(INFO, "RndB: %s", sprint_hex(RndB, 16)); + } + + memmove(raw, RndA, 16); + memmove(&raw[16], &RndB[1], 16); + + aes_encode(IVW, key, raw, &cmd2[1], 32); + if (verbose) { + PrintAndLogEx(INFO, ">phase2: %s", sprint_hex(cmd2, 33)); + } + } else { + aes_decode(NULL, key, &data[1], RndB, 16); + RndB[16] = RndB[0]; + if (verbose) { + PrintAndLogEx(INFO, "RndB: %s", sprint_hex(RndB, 16)); + } + + memmove(raw, RndA, 16); + memmove(&raw[16], &RndB[1], 16); + + aes_encode(NULL, key, raw, &cmd2[1], 32); + if (verbose) { + PrintAndLogEx(INFO, ">phase2: %s", sprint_hex(cmd2, 33)); + } } res = ExchangeRAW14a(cmd2, sizeof(cmd2), false, true, data, sizeof(data), &datalen, silentMode); @@ -304,21 +353,9 @@ int MifareAuth4(mf4Session_t *mf4session, const uint8_t *keyn, uint8_t *key, boo PrintAndLogEx(INFO, "< phase2: %s", sprint_hex(data, datalen)); } - aes_decode(NULL, key, &data[1], raw, 32); - - if (verbose) { - PrintAndLogEx(INFO, "res: %s", sprint_hex(raw, 32)); - PrintAndLogEx(INFO, "RndA`: %s", sprint_hex(&raw[4], 16)); - } - - if (memcmp(&raw[4], &RndA[1], 16)) { + if (data[0]!=0x90) { if (silentMode == false) { - PrintAndLogEx(ERR, "\nAuthentication FAILED. rnd is not equal"); - } - - if (verbose) { - PrintAndLogEx(ERR, "RndA reader: %s", sprint_hex(&RndA[1], 16)); - PrintAndLogEx(ERR, "RndA card: %s", sprint_hex(&raw[4], 16)); + PrintAndLogEx(ERR, "\nAuthentication FAILED. Card did not ACK response"); } if (dropFieldIfError) { @@ -327,7 +364,19 @@ int MifareAuth4(mf4Session_t *mf4session, const uint8_t *keyn, uint8_t *key, boo return PM3_EWRONGANSWER; } + if (nonfirst) { aes_decode(IVR, key, &data[1], raw, 16); } else { aes_decode(NULL, key, &data[1], raw, 32); } + if (verbose) { + if (nonfirst) { + PrintAndLogEx(INFO, "res: %s", sprint_hex(raw, 16)); + PrintAndLogEx(INFO, "RndA`: %s", sprint_hex(&raw[0], 16)); + } else { + PrintAndLogEx(INFO, "res: %s", sprint_hex(raw, 32)); + PrintAndLogEx(INFO, "RndA`: %s", sprint_hex(&raw[4], 16)); + } + } + + if (verbose && !nonfirst) { PrintAndLogEx(INFO, " TI: %s", sprint_hex(raw, 4)); PrintAndLogEx(INFO, "pic: %s", sprint_hex(&raw[20], 6)); PrintAndLogEx(INFO, "pcd: %s", sprint_hex(&raw[26], 6)); @@ -377,11 +426,13 @@ int MifareAuth4(mf4Session_t *mf4session, const uint8_t *keyn, uint8_t *key, boo memmove(mf4session->RndA, RndA, 16); memmove(mf4session->RndB, RndB, 16); memmove(mf4session->Key, key, 16); - memmove(mf4session->TI, raw, 4); - memmove(mf4session->PICCap2, &raw[20], 6); - memmove(mf4session->PCDCap2, &raw[26], 6); memmove(mf4session->Kenc, kenc, 16); memmove(mf4session->Kmac, kmac, 16); + if (!nonfirst) { + memmove(mf4session->TI, raw, 4); + memmove(mf4session->PICCap2, &raw[20], 6); + memmove(mf4session->PCDCap2, &raw[26], 6); + } } if (verbose) { @@ -505,7 +556,7 @@ int mfpReadSector(uint8_t sectorNo, uint8_t keyType, uint8_t *key, uint8_t *data } mf4Session_t _session; - int res = MifareAuth4(&_session, keyn, key, true, true, true, verbose, false); + int res = MifareAuth4(&_session, keyn, key, false, true, true, true, verbose, false); if (res) { PrintAndLogEx(ERR, "Sector %u authentication error: %d", sectorNo, res); return res; @@ -535,7 +586,7 @@ int mfpReadSector(uint8_t sectorNo, uint8_t keyType, uint8_t *key, uint8_t *data } // Encrypted mode is always used. Doing an if to check will waste instructions - mfp_data_crypt(&_session, &data[1], &data[1], true); + mfp_data_crypt(&_session, &data[1], &data[1], true, 1); memcpy(&dataout[(n - firstBlockNo) * 16], &data[1], 16); From 7164cea9c07b5d4bf56f1115b746ff0fb032572a Mon Sep 17 00:00:00 2001 From: team-orangeBlue <63470411+team-orangeBlue@users.noreply.github.com> Date: Fri, 29 May 2026 12:35:42 +0300 Subject: [PATCH 04/11] Declare function Add support for non-first auth Signed-off-by: team-orangeBlue <63470411+team-orangeBlue@users.noreply.github.com> --- client/src/mifare/mifare4.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/mifare/mifare4.h b/client/src/mifare/mifare4.h index fedb482a4..f510df350 100644 --- a/client/src/mifare/mifare4.h +++ b/client/src/mifare/mifare4.h @@ -60,7 +60,7 @@ void mfpSetVerboseMode(bool verbose); const char *mfpGetErrorDescription(uint8_t errorCode); int CalculateMAC(mf4Session_t *mf4session, MACType_t mtype, uint8_t blockNum, uint8_t blockCount, uint8_t *data, int datalen, uint8_t *mac, bool verbose); -int MifareAuth4(mf4Session_t *mf4session, const uint8_t *keyn, uint8_t *key, bool activateField, bool leaveSignalON, bool dropFieldIfError, bool verbose, bool silentMode); +int MifareAuth4(mf4Session_t *mf4session, const uint8_t *keyn, uint8_t *key, bool nonfirst, bool activateField, bool leaveSignalON, bool dropFieldIfError, bool verbose, bool silentMode); int MFPWritePerso(const uint8_t *keyNum, const uint8_t *key, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen); int MFPCommitPerso(bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen); From b3fc5848b1e594187743df769dd8dd4a8abb7913 Mon Sep 17 00:00:00 2001 From: team-orangeBlue <63470411+team-orangeBlue@users.noreply.github.com> Date: Fri, 29 May 2026 12:36:50 +0300 Subject: [PATCH 05/11] Add block count support mfp_data_crypt can now work with read range Signed-off-by: team-orangeBlue <63470411+team-orangeBlue@users.noreply.github.com> --- client/src/cmdhfmfp.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/cmdhfmfp.h b/client/src/cmdhfmfp.h index 377525c03..3edf18bed 100644 --- a/client/src/cmdhfmfp.h +++ b/client/src/cmdhfmfp.h @@ -33,5 +33,5 @@ typedef struct mfp_keys { int CmdHFMFP(const char *Cmd); int CmdHFMFPNDEFRead(const char *Cmd); -int mfp_data_crypt(mf4Session_t *mf4session, uint8_t *dati, uint8_t *dato, bool rev); +int mfp_data_crypt(mf4Session_t *mf4session, uint8_t *dati, uint8_t *dato, bool rev, int bc); #endif From e6c29a9301798af880db2fdc84846a144d970e13 Mon Sep 17 00:00:00 2001 From: team-orangeBlue <63470411+team-orangeBlue@users.noreply.github.com> Date: Fri, 29 May 2026 12:46:16 +0300 Subject: [PATCH 06/11] Support auth4 All commands now do AuthFirst with the ability to change to NonFirst Signed-off-by: team-orangeBlue <63470411+team-orangeBlue@users.noreply.github.com> --- client/src/cmdhfmfp.c | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/client/src/cmdhfmfp.c b/client/src/cmdhfmfp.c index 18d326229..894b9b6f0 100644 --- a/client/src/cmdhfmfp.c +++ b/client/src/cmdhfmfp.c @@ -779,7 +779,7 @@ static int CmdHFMFPAuth(const char *Cmd) { CLIParserInit(&ctx, "hf mfp auth", "Executes AES authentication command for MIFARE Plus card", "hf mfp auth --ki 4000 --key 000102030405060708090a0b0c0d0e0f -> executes authentication\n" - "hf mfp auth --ki 9003 --key FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -v -> executes authentication and shows all the system data" + "hf mfp auth --ki 9003 --key FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -v -> upgrades tag to SL3 and shows all the system data" ); void *argtable[] = { @@ -806,10 +806,13 @@ static int CmdHFMFPAuth(const char *Cmd) { return PM3_EINVARG; } - return MifareAuth4(NULL, keyn, key, true, false, true, verbose, false); + int resBuffer = MifareAuth4(NULL, keyn, key, false, true, false, true, verbose, false); + if (resBuffer == PM3_SUCCESS && keyn[0] == 0x90 && (keyn[1] == 0x02 || keyn[1] == 0x03)) + PrintAndLogEx(INFO, "Switched security level ( " _GREEN_("ok") " )"); + return resBuffer; } -int mfp_data_crypt(mf4Session_t *mf4session, uint8_t *dati, uint8_t *dato, bool rev) { +int mfp_data_crypt(mf4Session_t *mf4session, uint8_t *dati, uint8_t *dato, bool rev, int bc) { uint8_t kenc[MFBLOCK_SIZE]; memcpy(kenc, mf4session->Kenc, MFBLOCK_SIZE); @@ -841,9 +844,9 @@ int mfp_data_crypt(mf4Session_t *mf4session, uint8_t *dati, uint8_t *dato, bool } if (rev) { - aes_decode(IV, kenc, dati, dato, MFBLOCK_SIZE); + aes_decode(IV, kenc, dati, dato, MFBLOCK_SIZE*bc); } else { - aes_encode(IV, kenc, dati, dato, MFBLOCK_SIZE); + aes_encode(IV, kenc, dati, dato, MFBLOCK_SIZE*bc); } return PM3_SUCCESS; @@ -921,7 +924,7 @@ static int CmdHFMFPRdbl(const char *Cmd) { } mf4Session_t mf4session; - int res = MifareAuth4(&mf4session, keyn, key, true, true, true, verbose, false); + int res = MifareAuth4(&mf4session, keyn, key, false, true, true, true, verbose, false); if (res) { PrintAndLogEx(ERR, "Authentication error: %d", res); return res; @@ -947,7 +950,7 @@ static int CmdHFMFPRdbl(const char *Cmd) { } if (plain == false) { - mfp_data_crypt(&mf4session, &data[1], &data[1], true); + mfp_data_crypt(&mf4session, &data[1], &data[1], true, blocksCount); } uint8_t sector = mfSectorNum(blockn); @@ -1030,7 +1033,7 @@ static int CmdHFMFPRdsc(const char *Cmd) { } mf4Session_t mf4session; - int res = MifareAuth4(&mf4session, keyn, key, true, true, true, verbose, false); + int res = MifareAuth4(&mf4session, keyn, key, false, true, true, true, verbose, false); if (res) { PrintAndLogEx(ERR, "Authentication error: %d", res); return res; @@ -1216,7 +1219,7 @@ static int CmdHFMFPWrbl(const char *Cmd) { } if (plain == false) { - mfp_data_crypt(&mf4session, &datain[0], &datain[0], false); + mfp_data_crypt(&mf4session, &datain[0], &datain[0], false, 1); } uint8_t data[250] = {0}; @@ -1333,13 +1336,13 @@ static int CmdHFMFPChKey(const char *Cmd) { PrintAndLogEx(INFO, "--key index:", sprint_hex(keyn, 2)); } - int res = MifareAuth4(&mf4session, keyn, key, true, true, true, verbose, false); + int res = MifareAuth4(&mf4session, keyn, key, false, true, true, true, verbose, false); if (res) { PrintAndLogEx(ERR, "Authentication error: %d", res); return res; } - mfp_data_crypt(&mf4session, &datain[0], &datain[0], false); + mfp_data_crypt(&mf4session, &datain[0], &datain[0], false, 1); uint8_t data[250] = {0}; int datalen = 0; @@ -1451,13 +1454,13 @@ static int CmdHFMFPChConf(const char *Cmd) { PrintAndLogEx(INFO, "--key index:", sprint_hex(keyn, 2)); } - int res = MifareAuth4(&mf4session, keyn, key, true, true, true, verbose, false); + int res = MifareAuth4(&mf4session, keyn, key, false, true, true, true, verbose, false); if (res) { PrintAndLogEx(ERR, "Authentication error: %d", res); return res; } - mfp_data_crypt(&mf4session, &datain[0], &datain[0], false); + mfp_data_crypt(&mf4session, &datain[0], &datain[0], false, 1); uint8_t data[250] = {0}; int datalen = 0; @@ -1548,7 +1551,7 @@ static int plus_key_check(uint8_t start_sector, uint8_t end_sector, uint8_t star // authentication loop with retries for (int retry = 0; retry < MFP_CHK_KEY_TRIES; retry++) { - res = MifareAuth4(NULL, keyn, currkey, selectCard, true, false, false, true); + res = MifareAuth4(NULL, keyn, currkey, false, selectCard, true, false, false, true); if (res == PM3_SUCCESS || res == PM3_EWRONGANSWER) { break; } From 3c3d0e275cca7408f596e845a988723ec638649d Mon Sep 17 00:00:00 2001 From: team-orangeBlue <63470411+team-orangeBlue@users.noreply.github.com> Date: Fri, 29 May 2026 12:49:43 +0300 Subject: [PATCH 07/11] Compiler fix Signed-off-by: team-orangeBlue <63470411+team-orangeBlue@users.noreply.github.com> --- client/src/cmdhfmfp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/src/cmdhfmfp.c b/client/src/cmdhfmfp.c index 894b9b6f0..8ca57b3b8 100644 --- a/client/src/cmdhfmfp.c +++ b/client/src/cmdhfmfp.c @@ -1067,7 +1067,7 @@ static int CmdHFMFPRdsc(const char *Cmd) { } if (plain == false) { - mfp_data_crypt(&mf4session, &data[1], &data[1], true); + mfp_data_crypt(&mf4session, &data[1], &data[1], true, 1); } mf_print_block_one(blockno, data + 1, verbose); @@ -1212,7 +1212,7 @@ static int CmdHFMFPWrbl(const char *Cmd) { } mf4Session_t mf4session; - int res = MifareAuth4(&mf4session, keyn, key, true, true, true, verbose, false); + int res = MifareAuth4(&mf4session, keyn, key, false, true, true, true, verbose, false); if (res) { PrintAndLogEx(ERR, "Authentication error: %d", res); return res; From 021ec18506f039d119be978e854e1bf74d9dbdbe Mon Sep 17 00:00:00 2001 From: team-orangeBlue <63470411+team-orangeBlue@users.noreply.github.com> Date: Fri, 29 May 2026 13:05:00 +0300 Subject: [PATCH 08/11] Add quickdump support Mifare Plus can now be dumped quickly. This is achieved by using a mix of READ RANGE, skipping reply MACs, requesting data in plaintext (almost) as much as possible, and also avoiding card + crypto restarts. 1K tags now require 3 seconds; 2k require just shy of 6; 4k require 8. Previously the time for 2k tags was 9 seconds. While the improvement is pretty small, it helps out. Signed-off-by: team-orangeBlue <63470411+team-orangeBlue@users.noreply.github.com> --- client/src/cmdhfmfp.c | 276 +++++++++++++++++++++++++++++++++--------- 1 file changed, 216 insertions(+), 60 deletions(-) diff --git a/client/src/cmdhfmfp.c b/client/src/cmdhfmfp.c index 8ca57b3b8..2b3ec9093 100644 --- a/client/src/cmdhfmfp.c +++ b/client/src/cmdhfmfp.c @@ -2026,6 +2026,7 @@ static int CmdHFMFPDump(const char *Cmd) { arg_str0(NULL, "keys", "", "AES key file from `hf mfp chk --dump` (JSON)"), arg_str0("k", "key", "", "AES key for all sectors (16 hex bytes)"), arg_str0(NULL, "mfc-keys", "", "MFC key file for SL1 sectors (.bin from `hf mf chk`)"), + arg_lit0(NULL, "1k", "Chip is 1KB in size (Mifare Plus SE)"), arg_lit0(NULL, "ns", "No save to file"), arg_lit0("v", "verbose", "Verbose output"), arg_param_end @@ -2048,8 +2049,9 @@ static int CmdHFMFPDump(const char *Cmd) { char mfc_key_fn[FILE_PATH_SIZE] = {0}; CLIParamStrToBuf(arg_get_str(ctx, 4), (uint8_t *)mfc_key_fn, FILE_PATH_SIZE, &mfckeyfnlen); - bool nosave = arg_get_lit(ctx, 5); - bool verbose = arg_get_lit(ctx, 6); + bool SE = arg_get_lit(ctx, 5); + bool nosave = arg_get_lit(ctx, 6); + bool verbose = arg_get_lit(ctx, 7); CLIParserFree(ctx); @@ -2074,6 +2076,8 @@ static int CmdHFMFPDump(const char *Cmd) { uint8_t numSectors; if (ATQA & 0x0002) { numSectors = MIFARE_4K_MAXSECTOR; // 40 sectors (4K) + } else if (SE) { + numSectors = MIFARE_1K_MAXSECTOR; // 16 sectors (1K) } else { numSectors = MIFARE_2K_MAXSECTOR; // 32 sectors (2K) } @@ -2082,7 +2086,7 @@ static int CmdHFMFPDump(const char *Cmd) { PrintAndLogEx(INFO, "UID......... " _GREEN_("%s"), sprint_hex(card.uid, card.uidlen)); PrintAndLogEx(INFO, "ATQA........ " _GREEN_("%02X %02X"), card.atqa[1], card.atqa[0]); PrintAndLogEx(INFO, "SAK......... " _GREEN_("%02X"), card.sak); - PrintAndLogEx(INFO, "Sectors..... " _GREEN_("%u") " (%s)", numSectors, (numSectors == MIFARE_4K_MAXSECTOR) ? "4K" : "2K"); + PrintAndLogEx(INFO, "Sectors..... " _GREEN_("%u") " (%s)", numSectors, (numSectors == MIFARE_4K_MAXSECTOR) ? "4K" : (numSectors == MIFARE_1K_MAXSECTOR) ? "1K" : "2K"); PrintAndLogEx(NORMAL, ""); // ======================================== @@ -2193,6 +2197,7 @@ static int CmdHFMFPDump(const char *Cmd) { // ======================================== // Determine SL for each sector based on which keys are available + bool quickread = true; // Reset if an SL1 key exists uint8_t sectorSL[64]; memset(sectorSL, MFP_SL_UNKNOWN, sizeof(sectorSL)); for (uint8_t s = 0; s < numSectors; s++) { @@ -2201,6 +2206,7 @@ static int CmdHFMFPDump(const char *Cmd) { } if (mfcFoundKeys[MF_KEY_A][s][0] || mfcFoundKeys[MF_KEY_B][s][0]) { sectorSL[s] = MFP_SL_1; + quickread = false; } } @@ -2224,65 +2230,211 @@ static int CmdHFMFPDump(const char *Cmd) { int sectorsRead = 0; int sl3Count = 0; int sl1Count = 0; - - for (uint8_t s = 0; s < numSectors; s++) { - - if (kbd_enter_pressed()) { - PrintAndLogEx(WARNING, "\naborted via keyboard"); - break; + uint64_t t1 = msclock(); + if (quickread) { // Auth to all sectors at once and read them out as quick as possible by analyzing ACLs + making the tag do as little crypto as possible + mf4Session_t _session; + // Cycle prep + uint8_t ki_pA[2] = {0x40, 0x00}; + uint8_t ki_pB[2] = {0x40, 0x01}; + uint8_t nullBlock[16] = {0}; + uint8_t nullChunk[48] = {0}; + // Partially unlock tag with all A keys for ACL reads + bool nonfirst = false; + uint8_t STBuffer[120]; + int STRead; + uint8_t mac[8] = {0}; +chunkCycle: + ki_pA[1] = sl3Count*2; + MifareAuth4(&_session, ki_pA, &aesFoundKeys[MF_KEY_A][sl3Count][1], nonfirst, !nonfirst, true, true, verbose, false); + nonfirst = true; + MFPReadBlock(&_session, false, false, true, 3+sl3Count*4, 1, false, true, STBuffer, sizeof(STBuffer), &STRead, mac); + if (STRead && STBuffer[0] != 0x90) { + PrintAndLogEx(ERR, "\nTrailer read error: %02x %s", STBuffer[0], mfpGetErrorDescription(STBuffer[0])); + goto chunkBlank; } - - bool readOK = false; - uint16_t blockOffset = mfFirstBlockOfSector(s); - uint8_t blocksInSector = mfNumBlocksPerSector(s); - - if (sectorSL[s] == MFP_SL_3) { - // --- Try SL3 (AES) --- - for (uint8_t kt = MF_KEY_A; kt <= MF_KEY_B && !readOK; kt++) { - if (aesFoundKeys[kt][s][0] == 0) { - continue; - } - - uint8_t sector_data[16 * 16] = {0}; - res = mfpReadSector(s, kt, &aesFoundKeys[kt][s][1], sector_data, verbose); - if (res == PM3_SUCCESS) { - memcpy(carddata + (blockOffset * MFBLOCK_SIZE), sector_data, blocksInSector * MFBLOCK_SIZE); - sectorRead[s] = 1; - readOK = true; - sectorsRead++; - sl3Count++; - } else if (verbose) { - PrintAndLogEx(DEBUG, "Sector %u SL3 key%s failed: %d", s, (kt == MF_KEY_A) ? "A" : "B", res); - } - } - } else if (sectorSL[s] == MFP_SL_1) { - // --- Try SL1 (CRYPTO1) --- - DropField(); - for (uint8_t kt = MF_KEY_A; kt <= MF_KEY_B && !readOK; kt++) { - if (mfcFoundKeys[kt][s][0] == 0) { - continue; - } - - uint8_t sector_data[16 * 16] = {0}; - res = mfp_read_sector_sl1(s, kt, &mfcFoundKeys[kt][s][1], sector_data, verbose); - if (res == PM3_SUCCESS) { - memcpy(carddata + (blockOffset * MFBLOCK_SIZE), sector_data, blocksInSector * MFBLOCK_SIZE); - sectorRead[s] = 1; - readOK = true; - sectorsRead++; - sl1Count++; - } else if (verbose) { - PrintAndLogEx(DEBUG, "Sector %u SL1 key%s failed: %d", s, (kt == MF_KEY_A) ? "A" : "B", res); - } - } + if (STRead != 1 + 16 + 2) { + PrintAndLogEx(ERR, "Error return length: %d", STRead); } - - if (readOK) { - PrintAndLogEx(INPLACE, "Reading sector %3d / %3d ( " _GREEN_("ok, %s") " )", - s, numSectors - 1, - (sectorSL[s] == MFP_SL_3) ? "SL3" : "SL1"); + mfp_data_crypt(&_session, &STBuffer[1], &STBuffer[1], true, 1); + // Multiblock reads do not allow reading out STs, as such this is the time to save them into the final dump + memcpy(carddata + ((3+4*sl3Count) * MFBLOCK_SIZE), &STBuffer[1], 1 * MFBLOCK_SIZE); + // Check if any block is encrypted only + ki_pB[1] = 0x01+sl3Count*2; + MifareAuth4(&_session, ki_pB, &aesFoundKeys[MF_KEY_B][sl3Count][1], true, false, true, true, verbose, false); + if (STBuffer[6] & 0xF0) { // At least one bit is set to force enc. only + MFPReadBlock(&_session, false, false, true, sl3Count*4, 3, false, true, STBuffer, sizeof(STBuffer), &STRead, mac); + if (STRead && STBuffer[0] != 0x90) { +chunkBlank: + PrintAndLogEx(ERR, "\nChunk read error: %02x %s", STBuffer[0], mfpGetErrorDescription(STBuffer[0])); + memcpy(carddata + (sl3Count * 4 * MFBLOCK_SIZE), nullChunk, 3 * MFBLOCK_SIZE); + memcpy(carddata + ((3+4*sl3Count) * MFBLOCK_SIZE), nullBlock, 1 * MFBLOCK_SIZE); + PrintAndLogEx(WARNING, "Quick-reading sector %3d / %3d ( " _RED_("fail") " )", sl3Count, numSectors - 1); + // Restart auth since a read failure resets it + nonfirst = false; + sl3Count++; + goto chunkCycleClean; + } + if (STRead != 1 + 48 + 2) { + PrintAndLogEx(ERR, "Error return length: %d", STRead); + DropField(); + return PM3_ESOFT; + } + mfp_data_crypt(&_session, &STBuffer[1], &STBuffer[1], true, 3); + PrintAndLogEx(INPLACE, "Quick-reading sector %3d / %3d ( " _GREEN_("ok") " )", sl3Count, numSectors - 1); + memcpy(carddata + (sl3Count * 4 * MFBLOCK_SIZE), &STBuffer[1], 3 * MFBLOCK_SIZE); + sectorsRead++; + sl3Count++; } else { - PrintAndLogEx(INPLACE, "Reading sector %3d / %3d ( " _RED_("fail") " )", s, numSectors - 1); + MFPReadBlock(&_session, true, false, true, sl3Count*4, 3, false, true, STBuffer, sizeof(STBuffer), &STRead, mac); + if (STRead && STBuffer[0] != 0x90) { + PrintAndLogEx(ERR, "Chunk read error: %02x %s", STBuffer[0], mfpGetErrorDescription(STBuffer[0])); + memcpy(carddata + (sl3Count * 4 * MFBLOCK_SIZE), nullChunk, 3 * MFBLOCK_SIZE); + PrintAndLogEx(WARNING, "Quick-reading sector %3d / %3d ( " _RED_("fail") " )", sl3Count, numSectors - 1); + goto chunkCycle; + } + if (STRead != 1 + 48 + 2) { + PrintAndLogEx(ERR, "Error return length: %d", STRead); + DropField(); + return PM3_ESOFT; + } + PrintAndLogEx(INPLACE, "Quick-reading sector %3d / %3d ( " _GREEN_("ok") " )", sl3Count, numSectors - 1); + memcpy(carddata + (sl3Count * 4 * MFBLOCK_SIZE), &STBuffer[1], 3 * MFBLOCK_SIZE); + sectorsRead++; + sl3Count++; + } +chunkCycleClean: + if (sl3Count < MIN(32, numSectors)) goto chunkCycle; // Should stop at 16, or 32, or 32 then go to next cycle +// MifareAuth4(&_session, ki_pB, &aesFoundKeys[MF_KEY_B][0][1], true, false, true, true, verbose, false); + + + + if (numSectors>32){ +chunkCycle2: + ki_pA[1] = sl3Count*2; + MifareAuth4(&_session, ki_pA, &aesFoundKeys[MF_KEY_A][sl3Count][1], nonfirst, !nonfirst, true, true, verbose, false); + nonfirst = true; + MFPReadBlock(&_session, false, false, true, 128+(sl3Count-31)*16-1, 1, false, true, STBuffer, sizeof(STBuffer), &STRead, mac); + if (STRead && STBuffer[0] != 0x90) { + PrintAndLogEx(ERR, "Trailer read error: %02x %s", STBuffer[0], mfpGetErrorDescription(STBuffer[0])); + goto chunkBlank2; + return PM3_ESOFT; + } + if (STRead != 1 + 16 + 2) { + PrintAndLogEx(ERR, "Error return length: %d", STRead); + } + mfp_data_crypt(&_session, &STBuffer[1], &STBuffer[1], true, 1); + // Multiblock reads do not allow reading out STs, as such this is the time to save them into the final dump + memcpy(carddata + ((128+(sl3Count-31)*16-1) * MFBLOCK_SIZE), &STBuffer[1], 1 * MFBLOCK_SIZE); + int c = 0; + // Check if any block is encrypted only + ki_pB[1] = 0x01+sl3Count*2; + MifareAuth4(&_session, ki_pB, &aesFoundKeys[MF_KEY_B][sl3Count][1], true, false, true, true, verbose, false); + if (STBuffer[6] & 0xF0) { // At least one bit is set to force enc. only +chunkBlank2: // Jumping here will start the cycle which will blank out the remaining 5 chunks anyway + for (c=0; c<4; ++c) { + MFPReadBlock(&_session, false, false, true, 128+(sl3Count-32)*16+c*3, 3, false, true, STBuffer, sizeof(STBuffer), &STRead, mac); + if (STRead && STBuffer[0] != 0x90) { + PrintAndLogEx(ERR, "Chunk read error: %02x %s", STBuffer[0], mfpGetErrorDescription(STBuffer[0])); + PrintAndLogEx(WARNING, "Quick-reading sector %3d / %3d chunk %d ( " _RED_("fail") " )", sl3Count, numSectors - 1, c); + memcpy(carddata + ((128+(sl3Count-31)*16-1) * MFBLOCK_SIZE), nullBlock, 1 * MFBLOCK_SIZE); + memcpy(carddata + ((128 + (sl3Count-32)+c*3) * MFBLOCK_SIZE), nullChunk, 3 * MFBLOCK_SIZE); + nonfirst = false; + if (c<5) {continue;} else {sl3Count++; goto chunkCycleClean2;}; + } + if (STRead != 1 + 48 + 2) { + PrintAndLogEx(ERR, "Error return length: %d", STRead); + DropField(); + return PM3_ESOFT; + } + mfp_data_crypt(&_session, &STBuffer[1], &STBuffer[1], true, 3); + PrintAndLogEx(INPLACE, "Quick-reading sector %3d / %3d ( " _GREEN_("ok") " )", sl3Count, numSectors - 1); + memcpy(carddata + ((128 + (sl3Count-32)+c*3) * MFBLOCK_SIZE), &STBuffer[1], 3 * MFBLOCK_SIZE); + } + sectorsRead++; + sl3Count++; + } else { + for (c=0; c<4; ++c) { + MFPReadBlock(&_session, true, false, true, 128+(sl3Count-32)*16+c*3, 3, false, true, STBuffer, sizeof(STBuffer), &STRead, mac); + if (STRead && STBuffer[0] != 0x90) { + PrintAndLogEx(ERR, "Chunk read error: %02x %s", STBuffer[0], mfpGetErrorDescription(STBuffer[0])); + memcpy(carddata + ((128 + (sl3Count-32)+c*3) * MFBLOCK_SIZE), nullChunk, 3 * MFBLOCK_SIZE); + PrintAndLogEx(WARNING, "Quick-reading sector %3d / %3d ( " _RED_("fail") " )", sl3Count, numSectors - 1); + continue; + } + if (STRead != 1 + 48 + 2) { + PrintAndLogEx(ERR, "Error return length: %d", STRead); + DropField(); + return PM3_ESOFT; + } + PrintAndLogEx(INPLACE, "Quick-reading sector %3d / %3d ( " _GREEN_("ok") " )", sl3Count, numSectors - 1); + memcpy(carddata + ((128 + (sl3Count-32)+c*3) * MFBLOCK_SIZE), &STBuffer[1], 3 * MFBLOCK_SIZE); + + } + sectorsRead++; + sl3Count++; + } +chunkCycleClean2: + if (sl3Count < numSectors) goto chunkCycle2; + } + } else { + for (uint8_t s = 0; s < numSectors; s++) { + if (kbd_enter_pressed()) { + PrintAndLogEx(WARNING, "\naborted via keyboard"); + break; + } + + bool readOK = false; + uint16_t blockOffset = mfFirstBlockOfSector(s); + uint8_t blocksInSector = mfNumBlocksPerSector(s); + + if (sectorSL[s] == MFP_SL_3) { + // --- Try SL3 (AES) --- + for (uint8_t kt = MF_KEY_A; kt <= MF_KEY_B && !readOK; kt++) { + if (aesFoundKeys[kt][s][0] == 0) { + continue; + } + + uint8_t sector_data[16 * 16] = {0}; + res = mfpReadSector(s, kt, &aesFoundKeys[kt][s][1], sector_data, verbose); + if (res == PM3_SUCCESS) { + memcpy(carddata + (blockOffset * MFBLOCK_SIZE), sector_data, blocksInSector * MFBLOCK_SIZE); + sectorRead[s] = 1; + readOK = true; + sectorsRead++; + sl3Count++; + } else if (verbose) { + PrintAndLogEx(DEBUG, "Sector %u SL3 key%s failed: %d", s, (kt == MF_KEY_A) ? "A" : "B", res); + } + } + } else if (sectorSL[s] == MFP_SL_1) { + // --- Try SL1 (CRYPTO1) --- + DropField(); + for (uint8_t kt = MF_KEY_A; kt <= MF_KEY_B && !readOK; kt++) { + if (mfcFoundKeys[kt][s][0] == 0) { + continue; + } + + uint8_t sector_data[16 * 16] = {0}; + res = mfp_read_sector_sl1(s, kt, &mfcFoundKeys[kt][s][1], sector_data, verbose); + if (res == PM3_SUCCESS) { + memcpy(carddata + (blockOffset * MFBLOCK_SIZE), sector_data, blocksInSector * MFBLOCK_SIZE); + sectorRead[s] = 1; + readOK = true; + sectorsRead++; + sl1Count++; + } else if (verbose) { + PrintAndLogEx(DEBUG, "Sector %u SL1 key%s failed: %d", s, (kt == MF_KEY_A) ? "A" : "B", res); + } + } + } + + if (readOK) { + PrintAndLogEx(INPLACE, "Reading sector %3d / %3d ( " _GREEN_("ok, %s") " )", + s, numSectors - 1, + (sectorSL[s] == MFP_SL_3) ? "SL3" : "SL1"); + } else { + PrintAndLogEx(INPLACE, "Reading sector %3d / %3d ( " _RED_("fail") " )", s, numSectors - 1); + } } } @@ -2291,6 +2443,9 @@ static int CmdHFMFPDump(const char *Cmd) { PrintAndLogEx(INFO, "Successfully read " _GREEN_("%d") " / %d sectors (SL3: %d, SL1: %d)", sectorsRead, numSectors, sl3Count, sl1Count); PrintAndLogEx(NORMAL, ""); + DropField(); + t1 = msclock() - t1; + // ======================================== // Print sector summary // ======================================== @@ -2391,7 +2546,8 @@ static int CmdHFMFPDump(const char *Cmd) { PrintAndLogEx(HINT, "Partial dump: %d of %d sectors read", sectorsRead, numSectors); PrintAndLogEx(HINT, "Hint: Try " _YELLOW_("`hf mfp chk --dump`") " and/or " _YELLOW_("`hf mf chk`") " to find more keys"); } - + + PrintAndLogEx(INFO, "\ntime in dump " _YELLOW_("%.0f") " seconds\n", (float)t1 / 1000.0); free(carddata); return PM3_SUCCESS; } From 319a4d5bb0c613f85cc33c9f26fa1da37521bcb7 Mon Sep 17 00:00:00 2001 From: team-orangeBlue <63470411+team-orangeBlue@users.noreply.github.com> Date: Fri, 29 May 2026 13:08:34 +0300 Subject: [PATCH 09/11] Update CHANGELOG.md Signed-off-by: team-orangeBlue <63470411+team-orangeBlue@users.noreply.github.com> --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ad239553..830fb4a18 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,9 @@ 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 `hf mfp rdbl` when using "read multiple" blocks by decrypting the entire buffer instead of one block only (@team-orangeBlue) +- Improved `hf mfp dump` execution speed by removing crypto+card restarts, approx. 40% faster (@team-orangeBlue) +- Added support for non-first authentication in Mifare Plus (@team-orangeBlue) - Added CUDA version of tools/mfulc_des_brute (@C2Pwn) - Added `hf mfu desbrute` command: native client support for ULC key recovery (@C2Pwn) - Added `hf mf sen` command: native client support for FM11RF08S SEN recovery (@C2Pwn) From ee14761c22d889be0a9a656a427198bc412d0aa7 Mon Sep 17 00:00:00 2001 From: team-orangeBlue <63470411+team-orangeBlue@users.noreply.github.com> Date: Fri, 29 May 2026 13:27:09 +0300 Subject: [PATCH 10/11] Fix MAC calculation Doing non-first auth would reset counters, breaking MAC calculation and crypto as a whole Signed-off-by: team-orangeBlue <63470411+team-orangeBlue@users.noreply.github.com> --- client/src/mifare/mifare4.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/src/mifare/mifare4.c b/client/src/mifare/mifare4.c index b2065af6c..22bddba23 100644 --- a/client/src/mifare/mifare4.c +++ b/client/src/mifare/mifare4.c @@ -420,8 +420,6 @@ int MifareAuth4(mf4Session_t *mf4session, const uint8_t *keyn, uint8_t *key, boo if (mf4session) { mf4session->Authenticated = true; - mf4session->R_Ctr = 0; - mf4session->W_Ctr = 0; mf4session->KeyNum = keyn[1] + (keyn[0] << 8); memmove(mf4session->RndA, RndA, 16); memmove(mf4session->RndB, RndB, 16); @@ -429,6 +427,8 @@ int MifareAuth4(mf4Session_t *mf4session, const uint8_t *keyn, uint8_t *key, boo memmove(mf4session->Kenc, kenc, 16); memmove(mf4session->Kmac, kmac, 16); if (!nonfirst) { + mf4session->R_Ctr = 0; + mf4session->W_Ctr = 0; memmove(mf4session->TI, raw, 4); memmove(mf4session->PICCap2, &raw[20], 6); memmove(mf4session->PCDCap2, &raw[26], 6); From d859b224d2737ded4ea5299eea42317eee088364 Mon Sep 17 00:00:00 2001 From: team-orangeBlue <63470411+team-orangeBlue@users.noreply.github.com> Date: Fri, 29 May 2026 15:33:44 +0300 Subject: [PATCH 11/11] Fix 4K dumping A missed multiplier broke dump saving Signed-off-by: team-orangeBlue <63470411+team-orangeBlue@users.noreply.github.com> --- client/src/cmdhfmfp.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/client/src/cmdhfmfp.c b/client/src/cmdhfmfp.c index 2b3ec9093..002742c0c 100644 --- a/client/src/cmdhfmfp.c +++ b/client/src/cmdhfmfp.c @@ -2331,16 +2331,16 @@ chunkCycle2: MifareAuth4(&_session, ki_pB, &aesFoundKeys[MF_KEY_B][sl3Count][1], true, false, true, true, verbose, false); if (STBuffer[6] & 0xF0) { // At least one bit is set to force enc. only chunkBlank2: // Jumping here will start the cycle which will blank out the remaining 5 chunks anyway - for (c=0; c<4; ++c) { + for (c=0; c<5; ++c) { MFPReadBlock(&_session, false, false, true, 128+(sl3Count-32)*16+c*3, 3, false, true, STBuffer, sizeof(STBuffer), &STRead, mac); if (STRead && STBuffer[0] != 0x90) { PrintAndLogEx(ERR, "Chunk read error: %02x %s", STBuffer[0], mfpGetErrorDescription(STBuffer[0])); PrintAndLogEx(WARNING, "Quick-reading sector %3d / %3d chunk %d ( " _RED_("fail") " )", sl3Count, numSectors - 1, c); memcpy(carddata + ((128+(sl3Count-31)*16-1) * MFBLOCK_SIZE), nullBlock, 1 * MFBLOCK_SIZE); - memcpy(carddata + ((128 + (sl3Count-32)+c*3) * MFBLOCK_SIZE), nullChunk, 3 * MFBLOCK_SIZE); + memcpy(carddata + ((128 + 16*(sl3Count-32)+c*3) * MFBLOCK_SIZE), nullChunk, 3 * MFBLOCK_SIZE); nonfirst = false; if (c<5) {continue;} else {sl3Count++; goto chunkCycleClean2;}; - } + } if (STRead != 1 + 48 + 2) { PrintAndLogEx(ERR, "Error return length: %d", STRead); DropField(); @@ -2348,26 +2348,26 @@ chunkBlank2: // Jumping here will start the cycle which will blank out the remai } mfp_data_crypt(&_session, &STBuffer[1], &STBuffer[1], true, 3); PrintAndLogEx(INPLACE, "Quick-reading sector %3d / %3d ( " _GREEN_("ok") " )", sl3Count, numSectors - 1); - memcpy(carddata + ((128 + (sl3Count-32)+c*3) * MFBLOCK_SIZE), &STBuffer[1], 3 * MFBLOCK_SIZE); + memcpy(carddata + ((128 + 16*(sl3Count-32)+c*3) * MFBLOCK_SIZE), &STBuffer[1], 3 * MFBLOCK_SIZE); } sectorsRead++; sl3Count++; } else { - for (c=0; c<4; ++c) { + for (c=0; c<5; ++c) { MFPReadBlock(&_session, true, false, true, 128+(sl3Count-32)*16+c*3, 3, false, true, STBuffer, sizeof(STBuffer), &STRead, mac); if (STRead && STBuffer[0] != 0x90) { PrintAndLogEx(ERR, "Chunk read error: %02x %s", STBuffer[0], mfpGetErrorDescription(STBuffer[0])); - memcpy(carddata + ((128 + (sl3Count-32)+c*3) * MFBLOCK_SIZE), nullChunk, 3 * MFBLOCK_SIZE); + memcpy(carddata + ((128 + 16*(sl3Count-32)+c*3) * MFBLOCK_SIZE), nullChunk, 3 * MFBLOCK_SIZE); PrintAndLogEx(WARNING, "Quick-reading sector %3d / %3d ( " _RED_("fail") " )", sl3Count, numSectors - 1); continue; - } + } if (STRead != 1 + 48 + 2) { PrintAndLogEx(ERR, "Error return length: %d", STRead); DropField(); return PM3_ESOFT; } PrintAndLogEx(INPLACE, "Quick-reading sector %3d / %3d ( " _GREEN_("ok") " )", sl3Count, numSectors - 1); - memcpy(carddata + ((128 + (sl3Count-32)+c*3) * MFBLOCK_SIZE), &STBuffer[1], 3 * MFBLOCK_SIZE); + memcpy(carddata + ((128 + 16*(sl3Count-32)+c*3) * MFBLOCK_SIZE), &STBuffer[1], 3 * MFBLOCK_SIZE); } sectorsRead++;