diff --git a/CHANGELOG.md b/CHANGELOG.md index 101242adf..aef0c3883 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] +- Changed `mem load` - now handles UL-C and UL-AES dictionary files (@iceman1001) +- Changed `hf mfu sim` - now support UL-C simulation (@iceman1001) +- Added `!` - run system commands from inside the client. Potentially dangerous if running client as SUDO, SU, ROOT (@iceman1001) ## [Daddy Iceman.4.20469][2025-06-16] - Fixed edge case in fm11rf08s key recovery tools (@doegox) diff --git a/armsrc/Standalone/hf_aveful.c b/armsrc/Standalone/hf_aveful.c index 626aa9d17..d634f819c 100644 --- a/armsrc/Standalone/hf_aveful.c +++ b/armsrc/Standalone/hf_aveful.c @@ -157,7 +157,7 @@ void RunMod(void) { if (button_pressed != BUTTON_NO_CLICK || data_available()) break; else if (state == STATE_SEARCH) { - if (!iso14443a_select_card(NULL, &card, NULL, true, 0, true)) { + if (iso14443a_select_card(NULL, &card, NULL, true, 0, true) == 0) { FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LED_D_OFF(); SpinDelay(500); @@ -246,7 +246,7 @@ void RunMod(void) { FLAG_SET_UID_IN_DATA(flags, 7); Dbprintf("Starting simulation, press " _GREEN_("pm3 button") " to stop and go back to search state."); - SimulateIso14443aTag(7, flags, card.uid, 0, NULL, 0); + SimulateIso14443aTag(7, flags, card.uid, 0, NULL, 0, false, false); // Go back to search state if user presses pm3-button state = STATE_SEARCH; diff --git a/armsrc/Standalone/hf_cardhopper.c b/armsrc/Standalone/hf_cardhopper.c index 167e5b52f..d199dcdab 100644 --- a/armsrc/Standalone/hf_cardhopper.c +++ b/armsrc/Standalone/hf_cardhopper.c @@ -234,7 +234,7 @@ static void become_card(void) { tag_response_info_t *canned; uint32_t cuid; uint8_t pages; - if (SimulateIso14443aInit(tagType, flags, data, NULL, 0, &canned, &cuid, &pages) == false) { + if (SimulateIso14443aInit(tagType, flags, data, NULL, 0, &canned, &cuid, &pages, NULL) == false) { DbpString(_RED_("Error initializing the emulation process!")); return; } diff --git a/armsrc/Standalone/hf_colin.c b/armsrc/Standalone/hf_colin.c index 423e093f6..0aa49c092 100644 --- a/armsrc/Standalone/hf_colin.c +++ b/armsrc/Standalone/hf_colin.c @@ -498,7 +498,7 @@ failtag: SpinOff(50); LED_A_ON(); - while (!iso14443a_select_card(colin_cjuid, &colin_p_card, &colin_cjcuid, true, 0, true)) { + while (iso14443a_select_card(colin_cjuid, &colin_p_card, &colin_cjcuid, true, 0, true) == 0) { WDT_HIT(); if (BUTTON_HELD(10) == BUTTON_HOLD) { WDT_HIT(); @@ -785,7 +785,7 @@ static int e_MifareECardLoad(uint32_t numofsectors, uint8_t keytype) { bool isOK = true; - if (!iso14443a_select_card(colin_cjuid, &colin_p_card, &colin_cjcuid, true, 0, true)) { + if (iso14443a_select_card(colin_cjuid, &colin_p_card, &colin_cjcuid, true, 0, true) == 0) { isOK = false; } @@ -844,8 +844,7 @@ static int cjat91_saMifareChkKeys(uint8_t blockNo, uint8_t keyType, bool clearTr for (uint8_t i = 0; i < keyCount; i++) { /* no need for anticollision. just verify tag is still here */ - // if (!iso14443a_fast_select_card(colin_cjuid, 0)) { - if (!iso14443a_select_card(colin_cjuid, &colin_p_card, &colin_cjcuid, true, 0, true)) { + if (iso14443a_select_card(colin_cjuid, &colin_p_card, &colin_cjcuid, true, 0, true) == 0) { cjSetCursLeft(); DbprintfEx(FLAG_NEWLINE, "%sFATAL%s : E_MF_LOSTTAG", _XRED_, _XWHITE_); break; @@ -963,7 +962,7 @@ static int saMifareCSetBlock(uint32_t arg0, uint32_t arg1, uint32_t arg2, const // get UID from chip if (workFlags & 0x01) { - if (!iso14443a_select_card(colin_cjuid, &colin_p_card, &colin_cjcuid, true, 0, true)) { + if (iso14443a_select_card(colin_cjuid, &colin_p_card, &colin_cjcuid, true, 0, true) == 0) { DbprintfEx(FLAG_NEWLINE, "Can't select card"); break; }; diff --git a/armsrc/Standalone/hf_craftbyte.c b/armsrc/Standalone/hf_craftbyte.c index 6eb2ae2a2..736e89aca 100644 --- a/armsrc/Standalone/hf_craftbyte.c +++ b/armsrc/Standalone/hf_craftbyte.c @@ -89,22 +89,22 @@ void RunMod(void) { Dbprintf("Starting simulation, press " _GREEN_("pm3 button") " to stop and go back to search state."); if (card.sak == 0x08 && card.atqa[0] == 0x04 && card.atqa[1] == 0) { DbpString("Mifare Classic 1k"); - SimulateIso14443aTag(1, flags, card.uid, 0, NULL, 0); + SimulateIso14443aTag(1, flags, card.uid, 0, NULL, 0, false, false); } else if (card.sak == 0x08 && card.atqa[0] == 0x44 && card.atqa[1] == 0) { DbpString("Mifare Classic 4k "); - SimulateIso14443aTag(8, flags, card.uid, 0, NULL, 0); + SimulateIso14443aTag(8, flags, card.uid, 0, NULL, 0, false, false); } else if (card.sak == 0x00 && card.atqa[0] == 0x44 && card.atqa[1] == 0) { DbpString("Mifare Ultralight"); - SimulateIso14443aTag(2, flags, card.uid, 0, NULL, 0); + SimulateIso14443aTag(2, flags, card.uid, 0, NULL, 0, false, false); } else if (card.sak == 0x20 && card.atqa[0] == 0x04 && card.atqa[1] == 0x03) { DbpString("Mifare DESFire"); - SimulateIso14443aTag(3, flags, card.uid, 0, NULL, 0); + SimulateIso14443aTag(3, flags, card.uid, 0, NULL, 0, false, false); } else if (card.sak == 0x20 && card.atqa[0] == 0x44 && card.atqa[1] == 0x03) { DbpString("Mifare DESFire Ev1/Plus/JCOP"); - SimulateIso14443aTag(3, flags, card.uid, 0, NULL, 0); + SimulateIso14443aTag(3, flags, card.uid, 0, NULL, 0, false, false); } else { Dbprintf("Unrecognized tag type -- defaulting to Mifare Classic emulation"); - SimulateIso14443aTag(1, flags, card.uid, 0, NULL, 0); + SimulateIso14443aTag(1, flags, card.uid, 0, NULL, 0, false, false); } // Go back to search state if user presses pm3-button diff --git a/armsrc/Standalone/hf_msdsal.c b/armsrc/Standalone/hf_msdsal.c index 5e36a92c5..6eb5b46b2 100644 --- a/armsrc/Standalone/hf_msdsal.c +++ b/armsrc/Standalone/hf_msdsal.c @@ -379,7 +379,7 @@ void RunMod(void) { BigBuf_free_keep_EM(); // tag type: 11 = ISO/IEC 14443-4 - javacard (JCOP) - if (SimulateIso14443aInit(11, flags, data, NULL, 0, &responses, &cuid, NULL) == false) { + if (SimulateIso14443aInit(11, flags, data, NULL, 0, &responses, &cuid, NULL, NULL) == false) { BigBuf_free_keep_EM(); reply_ng(CMD_HF_MIFARE_SIMULATE, PM3_EINIT, NULL, 0); DbpString(_RED_("Error initializing the emulation process!")); diff --git a/armsrc/Standalone/hf_reblay.c b/armsrc/Standalone/hf_reblay.c index 8ecba8cf4..30db64f41 100644 --- a/armsrc/Standalone/hf_reblay.c +++ b/armsrc/Standalone/hf_reblay.c @@ -268,7 +268,7 @@ void RunMod() { BigBuf_free_keep_EM(); // 4 = ISO/IEC 14443-4 - javacard (JCOP) - if (SimulateIso14443aInit(4, flags, data, NULL, 0, &responses, &cuid, NULL) == false) { + if (SimulateIso14443aInit(4, flags, data, NULL, 0, &responses, &cuid, NULL, NULL) == false) { BigBuf_free_keep_EM(); reply_ng(CMD_HF_MIFARE_SIMULATE, PM3_EINIT, NULL, 0); DbpString(_RED_("Error initializing the emulation process!")); diff --git a/armsrc/Standalone/hf_tcprst.c b/armsrc/Standalone/hf_tcprst.c index c2b3ff51d..d8ced29d4 100644 --- a/armsrc/Standalone/hf_tcprst.c +++ b/armsrc/Standalone/hf_tcprst.c @@ -191,7 +191,7 @@ void RunMod(void) { memcpy(data, stuid, sizeof(stuid)); - if (SimulateIso14443aInit(tagType, flags, data, NULL, 0, &responses, &cuid, &pages) == false) { + if (SimulateIso14443aInit(tagType, flags, data, NULL, 0, &responses, &cuid, &pages, NULL) == false) { BigBuf_free_keep_EM(); reply_ng(CMD_HF_MIFARE_SIMULATE, PM3_EINIT, NULL, 0); DbpString(_YELLOW_("!!") "Error initializing the simulation process!"); @@ -369,7 +369,7 @@ void RunMod(void) { memcpy(data, stuid, sizeof(stuid)); - if (SimulateIso14443aInit(tagType, flags, data, NULL, 0, &responses, &cuid, &pages) == false) { + if (SimulateIso14443aInit(tagType, flags, data, NULL, 0, &responses, &cuid, &pages, NULL) == false) { BigBuf_free_keep_EM(); reply_ng(CMD_HF_MIFARE_SIMULATE, PM3_EINIT, NULL, 0); DbpString(_YELLOW_("!!") "Error initializing the simulation process!"); diff --git a/armsrc/Standalone/hf_young.c b/armsrc/Standalone/hf_young.c index 62d215dba..83ad1999a 100644 --- a/armsrc/Standalone/hf_young.c +++ b/armsrc/Standalone/hf_young.c @@ -96,7 +96,7 @@ void RunMod(void) { } } - if (!iso14443a_select_card(NULL, &card[selected], NULL, true, 0, true)) { + if (iso14443a_select_card(NULL, &card[selected], NULL, true, 0, true) == 0) { FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LED_D_OFF(); SpinDelay(500); @@ -253,25 +253,25 @@ void RunMod(void) { if (uids[selected].sak == 0x08 && uids[selected].atqa[0] == 0x04 && uids[selected].atqa[1] == 0) { DbpString("Mifare Classic 1k"); - SimulateIso14443aTag(1, flags, data, 0, NULL, 0); + SimulateIso14443aTag(1, flags, data, 0, NULL, 0, false, false); } else if (uids[selected].sak == 0x18 && uids[selected].atqa[0] == 0x02 && uids[selected].atqa[1] == 0) { DbpString("Mifare Classic 4k (4b uid)"); - SimulateIso14443aTag(8, flags, data, 0, NULL, 0); + SimulateIso14443aTag(8, flags, data, 0, NULL, 0, false, false); } else if (uids[selected].sak == 0x08 && uids[selected].atqa[0] == 0x44 && uids[selected].atqa[1] == 0) { DbpString("Mifare Classic 4k (7b uid)"); - SimulateIso14443aTag(8, flags, data, 0, NULL, 0); + SimulateIso14443aTag(8, flags, data, 0, NULL, 0, false, false); } else if (uids[selected].sak == 0x00 && uids[selected].atqa[0] == 0x44 && uids[selected].atqa[1] == 0) { DbpString("Mifare Ultralight"); - SimulateIso14443aTag(2, flags, data, 0, NULL, 0); + SimulateIso14443aTag(2, flags, data, 0, NULL, 0, false, false); } else if (uids[selected].sak == 0x20 && uids[selected].atqa[0] == 0x04 && uids[selected].atqa[1] == 0x03) { DbpString("Mifare DESFire"); - SimulateIso14443aTag(3, flags, data, 0, NULL, 0); + SimulateIso14443aTag(3, flags, data, 0, NULL, 0, false, false); } else if (uids[selected].sak == 0x20 && uids[selected].atqa[0] == 0x44 && uids[selected].atqa[1] == 0x03) { DbpString("Mifare DESFire Ev1/Plus/JCOP"); - SimulateIso14443aTag(3, flags, data, 0, NULL, 0); + SimulateIso14443aTag(3, flags, data, 0, NULL, 0, false, false); } else { Dbprintf("Unrecognized tag type -- defaulting to Mifare Classic emulation"); - SimulateIso14443aTag(1, flags, data, 0, NULL, 0); + SimulateIso14443aTag(1, flags, data, 0, NULL, 0, false, false); } } else if (button_pressed == BUTTON_SINGLE_CLICK) { diff --git a/armsrc/appmain.c b/armsrc/appmain.c index 9399592c9..db16394ab 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -480,6 +480,32 @@ static void SendStatus(uint32_t wait) { } else { Dbprintf(" iClass... "_RED_("%u")" keys - "_RED_("%s"), num, ICLASS_KEYS_FILE); } + + if (exists_in_spiffs(MFULC_KEYS_FILE)) { + num = size_in_spiffs(MFULC_KEYS_FILE) / MFULC_KEY_LENGTH; + } else { + num = 0; + } + + if (num > 0) { + Dbprintf(" UL-C..... "_YELLOW_("%u")" keys - "_GREEN_("%s"), num, MFULC_KEYS_FILE); + } else { + Dbprintf(" UL-C..... "_RED_("%u")" keys - "_RED_("%s"), num, MFULC_KEYS_FILE); + } + + if (exists_in_spiffs(MFULAES_KEYS_FILE)) { + num = size_in_spiffs(MFULAES_KEYS_FILE) / MFULAES_KEY_LENGTH; + } else { + num = 0; + } + + if (num > 0) { + Dbprintf(" UL-AES... "_YELLOW_("%u")" keys - "_GREEN_("%s"), num, MFULAES_KEYS_FILE); + } else { + Dbprintf(" UL-AES... "_RED_("%u")" keys - "_RED_("%s"), num, MFULAES_KEYS_FILE); + } + + #endif DbpString(""); reply_ng(CMD_STATUS, PM3_SUCCESS, NULL, 0); @@ -1723,10 +1749,13 @@ static void PacketReceived(PacketCommandNG *packet) { uint8_t uid[10]; uint8_t exitAfter; uint8_t rats[20]; + bool ulc_p1; + bool ulc_p2; } PACKED; struct p *payload = (struct p *) packet->data.asBytes; SimulateIso14443aTag(payload->tagtype, payload->flags, payload->uid, - payload->exitAfter, payload->rats, sizeof(payload->rats)); // ## Simulate iso14443a tag - pass tag type & UID + payload->exitAfter, payload->rats, sizeof(payload->rats), + payload->ulc_p1, payload->ulc_p2); // ## Simulate iso14443a tag - pass tag type & UID break; } case CMD_HF_ISO14443A_SIM_AID: { diff --git a/armsrc/dbprint.c b/armsrc/dbprint.c index 42d96fc3e..687bdfb90 100644 --- a/armsrc/dbprint.c +++ b/armsrc/dbprint.c @@ -102,9 +102,7 @@ void Dbhexdump(int len, const uint8_t *d, bool bAsci) { } #endif } -void print_result(const char *name, const uint8_t *d, size_t - - n) { +void print_result(const char *name, const uint8_t *d, size_t n) { const uint8_t *p = d; uint16_t tmp = n & 0xFFF0; diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index 069aa87c8..4bab0a0b4 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -370,16 +370,16 @@ tUart14a *GetUart14a(void) { void Uart14aReset(void) { Uart.state = STATE_14A_UNSYNCD; + Uart.shiftReg = 0; // shiftreg to hold decoded data bits Uart.bitCount = 0; Uart.len = 0; // number of decoded data bytes - Uart.parityLen = 0; // number of decoded parity bytes - Uart.shiftReg = 0; // shiftreg to hold decoded data bits - Uart.parityBits = 0; // holds 8 parity bits - Uart.startTime = 0; - Uart.endTime = 0; - Uart.fourBits = 0x00000000; // clear the buffer for 4 Bits Uart.posCnt = 0; Uart.syncBit = 9999; + Uart.parityBits = 0; // holds 8 parity bits + Uart.parityLen = 0; // number of decoded parity bytes + Uart.fourBits = 0x00000000; // clear the buffer for 4 Bits + Uart.startTime = 0; + Uart.endTime = 0; } void Uart14aInit(uint8_t *d, uint16_t n, uint8_t *par) { @@ -1188,9 +1188,24 @@ bool prepare_allocated_tag_modulation(tag_response_info_t *response_info, uint8_ } } +static void Simulate_reread_ulc_key(uint8_t *ulc_key) { + // copy UL-C key from emulator memory + + mfu_dump_t *mfu_header = (mfu_dump_t *) BigBuf_get_EM_addr(); + + memcpy(ulc_key, mfu_header->data + (0x2D * 4), 4); + memcpy(ulc_key + 4, mfu_header->data + (0x2C * 4), 4); + memcpy(ulc_key + 8, mfu_header->data + (0x2F * 4), 4); + memcpy(ulc_key + 12, mfu_header->data + (0x2E * 4), 4); + + reverse_array(ulc_key, 4); + reverse_array(ulc_key + 4, 4); + reverse_array(ulc_key + 8, 4); + reverse_array(ulc_key + 12, 4); +} bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data, uint8_t *ats, size_t ats_len, tag_response_info_t **responses, - uint32_t *cuid, uint8_t *pages) { + uint32_t *cuid, uint8_t *pages, uint8_t *ulc_key) { uint8_t sak = 0; // The first response contains the ATQA (note: bytes are transmitted in reverse order). static uint8_t rATQA[2] = { 0x00 }; @@ -1340,6 +1355,38 @@ bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data, sak = 0x20; break; } + case 13: { // MIFARE Ultralight-C + + rATQA[0] = 0x44; + sak = 0x00; + + // some first pages of UL/NTAG dump is special data + mfu_dump_t *mfu_header = (mfu_dump_t *) BigBuf_get_EM_addr(); + *pages = MAX(mfu_header->pages, 47); + + // copy UL-C key from emulator memory + memcpy(ulc_key, mfu_header->data + (0x2D * 4), 4); + memcpy(ulc_key + 4, mfu_header->data + (0x2C * 4), 4); + memcpy(ulc_key + 8, mfu_header->data + (0x2F * 4), 4); + memcpy(ulc_key + 12, mfu_header->data + (0x2E * 4), 4); + + reverse_array(ulc_key, 4); + reverse_array(ulc_key + 4, 4); + reverse_array(ulc_key + 8, 4); + reverse_array(ulc_key + 12, 4); + + /* + Dbprintf("UL-C Pages....... %u ( 47 )", *pages); + DbpString("UL-C 3des key... "); + Dbhexdump(16, ulc_key, false); + */ + + if (IS_FLAG_UID_IN_DATA(flags, 7)) { + DbpString("UL-C UID........ "); + Dbhexdump(7, data, false); + } + break; + } default: { if (g_dbglevel >= DBG_ERROR) Dbprintf("Error: unknown tagtype (%d)", tagType); return false; @@ -1365,7 +1412,7 @@ bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data, // if uid not supplied then get from emulator memory if ((memcmp(data, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 10) == 0) || IS_FLAG_UID_IN_EMUL(flags)) { - if (tagType == 2 || tagType == 7) { + if (tagType == 2 || tagType == 7 || tagType == 13) { uint16_t start = MFU_DUMP_PREFIX_LENGTH; uint8_t emdata[8]; emlGet(emdata, start, sizeof(emdata)); @@ -1532,13 +1579,18 @@ bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data, // 'hf 14a sim' //----------------------------------------------------------------------------- void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *useruid, uint8_t exitAfterNReads, - uint8_t *ats, size_t ats_len) { + uint8_t *ats, size_t ats_len, bool ulc_part1, bool ulc_part2) { #define ATTACK_KEY_COUNT 16 +#define ULC_TAG_NONCE "\x01\x02\x03\x04\x05\x06\x07\x08" tag_response_info_t *responses; uint32_t cuid = 0; uint32_t nonce = 0; + /// Ultralight-C 3des2k + uint8_t ulc_key[16] = { 0x00 }; + uint8_t ulc_iv[8] = { 0x00 }; + bool ulc_reread_key = false; uint8_t pages = 0; // Here, we collect CUID, block1, keytype1, NT1, NR1, AR1, CUID, block2, keytyp2, NT2, NR2, AR2 @@ -1582,7 +1634,9 @@ void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *useruid, uin .modulation_n = 0 }; - if (SimulateIso14443aInit(tagType, flags, useruid, ats, ats_len, &responses, &cuid, &pages) == false) { + if (SimulateIso14443aInit(tagType, flags, useruid, ats, ats_len + , &responses, &cuid, &pages + , ulc_key) == false) { BigBuf_free_keep_EM(); reply_ng(CMD_HF_MIFARE_SIMULATE, PM3_EINIT, NULL, 0); return; @@ -1670,7 +1724,7 @@ void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *useruid, uin order = ORDER_NONE; // back to work state p_response = NULL; - } else if (order == ORDER_AUTH && len == 8) { + } else if (order == ORDER_AUTH && len == 8 && tagType != 2 && tagType != 7 && tagType != 13) { // Received {nr] and {ar} (part of authentication) LogTrace(receivedCmd, Uart.len, Uart.startTime * 16 - DELAY_AIR2ARM_AS_TAG, Uart.endTime * 16 - DELAY_AIR2ARM_AS_TAG, Uart.parity, true); uint32_t nr = bytes_to_num(receivedCmd, 4); @@ -1760,7 +1814,7 @@ void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *useruid, uin } else if (receivedCmd[0] == ISO14443A_CMD_READBLOCK && len == 4) { // Received a (plain) READ uint8_t block = receivedCmd[1]; // if Ultralight or NTAG (4 byte blocks) - if (tagType == 7 || tagType == 2) { + if (tagType == 7 || tagType == 2 || tagType == 13) { if (block > pages) { // send NACK 0x0 == invalid argument EmSend4bit(CARD_NACK_IV); @@ -1809,7 +1863,7 @@ void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *useruid, uin EmSendCmd(emdata, len + 2); } p_response = NULL; - } else if (receivedCmd[0] == MIFARE_ULC_WRITE && len == 8 && (tagType == 2 || tagType == 7)) { // Received a WRITE + } else if (receivedCmd[0] == MIFARE_ULC_WRITE && len == 8 && (tagType == 2 || tagType == 7 || tagType == 13)) { // Received a WRITE p_response = NULL; @@ -1847,12 +1901,15 @@ void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *useruid, uin // send ACK EmSend4bit(CARD_ACK); + if (tagType == 13 && block >= 0x2c && block <= 0x2F) { + ulc_reread_key = true; + } } else { // send NACK 0x1 == crc/parity error EmSend4bit(CARD_NACK_PA); } goto jump; - } else if (receivedCmd[0] == MIFARE_ULC_COMP_WRITE && len == 4 && (tagType == 2 || tagType == 7)) { + } else if (receivedCmd[0] == MIFARE_ULC_COMP_WRITE && len == 4 && (tagType == 2 || tagType == 7 || tagType == 13)) { // cmd + block + 2 bytes crc if (CheckCrc14A(receivedCmd, len)) { wrblock = receivedCmd[1]; @@ -1926,7 +1983,7 @@ void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *useruid, uin p_response = &responses[RESP_INDEX_VERSION]; } else if (receivedCmd[0] == MFDES_GET_VERSION && len == 4 && (tagType == 3)) { p_response = &responses[RESP_INDEX_VERSION]; - } else if ((receivedCmd[0] == MIFARE_AUTH_KEYA || receivedCmd[0] == MIFARE_AUTH_KEYB) && len == 4 && tagType != 2 && tagType != 7) { // Received an authentication request + } else if ((receivedCmd[0] == MIFARE_AUTH_KEYA || receivedCmd[0] == MIFARE_AUTH_KEYB) && len == 4 && tagType != 2 && tagType != 7 && tagType != 13) { // Received an authentication request cardAUTHKEY = receivedCmd[0] - 0x60; cardAUTHSC = receivedCmd[1] / 4; // received block num @@ -1945,9 +2002,84 @@ void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *useruid, uin } else { p_response = &responses[RESP_INDEX_ATS]; } - } else if (receivedCmd[0] == MIFARE_ULC_AUTH_1) { // ULC authentication, or Desfire Authentication - LogTrace(receivedCmd, Uart.len, Uart.startTime * 16 - DELAY_AIR2ARM_AS_TAG, Uart.endTime * 16 - DELAY_AIR2ARM_AS_TAG, Uart.parity, true); - p_response = NULL; + } else if (receivedCmd[0] == MIFARE_ULC_AUTH_1 && len == 4 && tagType == 13) { // ULC authentication, or Desfire Authentication + + // reset IV to all zeros + memset(ulc_iv, 0x00, 8); + + if (ulc_reread_key) { + Simulate_reread_ulc_key(ulc_key); + ulc_reread_key = false; + } + + dynamic_response_info.response[0] = MIFARE_ULC_AUTH_2; + + // our very random TAG NONCE + memcpy(dynamic_response_info.response + 1, ULC_TAG_NONCE, 8); + + if (ulc_part1) { + memset(dynamic_response_info.response + 1, 0, 8); + } else { + // encrypt TAG NONCE + tdes_nxp_send(dynamic_response_info.response + 1, dynamic_response_info.response + 1, 8, ulc_key, ulc_iv, 2); + } + + // Add CRC + AddCrc14A(dynamic_response_info.response, 9); + + // prepare to send + dynamic_response_info.response_n = 1 + 8 + 2; + prepare_tag_modulation(&dynamic_response_info, DYNAMIC_MODULATION_BUFFER_SIZE); + p_response = &dynamic_response_info; + order = ORDER_AUTH; + + } else if (receivedCmd[0] == MIFARE_ULC_AUTH_2 && len == 19 && tagType == 13) { // ULC authentication, or Desfire Authentication + + uint8_t enc_rnd_ab[16] = { 0x00 }; + uint8_t rnd_ab[16] = { 0x00 }; + + // copy reader response + memcpy(enc_rnd_ab, receivedCmd + 1, 16); + + // decrypt + tdes_nxp_receive(enc_rnd_ab, rnd_ab, 16, ulc_key, ulc_iv, 2); + + ror(rnd_ab + 8, 8); + + if (memcmp(rnd_ab + 8, ULC_TAG_NONCE, 8) != 0) { + Dbprintf("failed authentication"); + } + + // OK response + dynamic_response_info.response[0] = 0x00; + + if (ulc_part2) { + // try empty auth but with correct CRC and 0x00 command + memset(dynamic_response_info.response + 1, 0, 8); + } else { + // rol RndA + rol(rnd_ab, 8); + + // encrypt RndA + tdes_nxp_send(rnd_ab, dynamic_response_info.response + 1, 8, ulc_key, ulc_iv, 2); + } + + // Add CRC + AddCrc14A(dynamic_response_info.response, 9); + + dynamic_response_info.response_n = 1 + 8 + 2; + + prepare_tag_modulation(&dynamic_response_info, DYNAMIC_MODULATION_BUFFER_SIZE); + p_response = &dynamic_response_info; + order = ORDER_NONE; + // Add CRC + AddCrc14A(dynamic_response_info.response, 17); + + dynamic_response_info.response_n = 1 + 16 + 2; + + prepare_tag_modulation(&dynamic_response_info, DYNAMIC_MODULATION_BUFFER_SIZE); + p_response = &dynamic_response_info; + order = ORDER_NONE; } else if (receivedCmd[0] == MIFARE_ULEV1_AUTH && len == 7 && tagType == 7) { // NTAG / EV-1 uint8_t pwd[4] = {0, 0, 0, 0}; emlGet(pwd, (pages - 1) * 4 + MFU_DUMP_PREFIX_LENGTH, sizeof(pwd)); @@ -2128,13 +2260,16 @@ jump: // of bits specified in the delay parameter. static void PrepareDelayedTransfer(uint16_t delay) { delay &= 0x07; - if (!delay) return; + if (delay == 0) { + return; + } uint8_t bitmask = 0; uint8_t bits_shifted = 0; - for (uint16_t i = 0; i < delay; i++) + for (uint16_t i = 0; i < delay; i++) { bitmask |= (0x01 << i); + } tosend_t *ts = get_tosend(); @@ -2163,6 +2298,7 @@ static void TransmitFor14443a(const uint8_t *cmd, uint16_t len, uint32_t *timing Dbprintf("Warning: HF field is off"); return; } + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_READER_MOD); if (timing) { @@ -2337,7 +2473,7 @@ int EmGetCmd(uint8_t *received, uint16_t received_max_len, uint16_t *len, uint8_ // button press, takes a bit time, might mess with simualtion if (checker-- == 0) { if (BUTTON_PRESS()) { - Dbprintf("----------- " _GREEN_("Breaking / User aborted") " ----------"); + Dbprintf("----------- " _GREEN_("Button pressed, user aborted") " ----------"); return false; } @@ -2515,9 +2651,9 @@ int EmSendPrecompiledCmd(tag_response_info_t *p_response) { return ret; } -bool EmLogTrace(uint8_t *reader_data, uint16_t reader_len, uint32_t reader_StartTime, - uint32_t reader_EndTime, uint8_t *reader_Parity, uint8_t *tag_data, - uint16_t tag_len, uint32_t tag_StartTime, uint32_t tag_EndTime, uint8_t *tag_Parity) { +bool EmLogTrace(const uint8_t *reader_data, uint16_t reader_len, uint32_t reader_StartTime, + uint32_t reader_EndTime, const uint8_t *reader_Parity, const uint8_t *tag_data, + uint16_t tag_len, uint32_t tag_StartTime, uint32_t tag_EndTime, const uint8_t *tag_Parity) { // we cannot exactly measure the end and start of a received command from reader. However we know that the delay from // end of the received command to start of the tag's (simulated by us) answer is n*128+20 or n*128+84 resp. @@ -2851,7 +2987,7 @@ int iso14443a_select_card(uint8_t *uid_ptr, iso14a_card_select_t *p_card, uint32 // requests ATS unless no_rats is true int iso14443a_select_cardEx(uint8_t *uid_ptr, iso14a_card_select_t *p_card, uint32_t *cuid_ptr, bool anticollision, uint8_t num_cascades, bool no_rats, - iso14a_polling_parameters_t *polling_parameters) { + const iso14a_polling_parameters_t *polling_parameters) { uint8_t resp[MAX_FRAME_SIZE] = {0}; // theoretically. A usual RATS will be much smaller @@ -3106,7 +3242,7 @@ int iso14443a_select_cardEx(uint8_t *uid_ptr, iso14a_card_select_t *p_card, uint return 1; } -int iso14443a_fast_select_card(uint8_t *uid_ptr, uint8_t num_cascades) { +int iso14443a_fast_select_card(const uint8_t *uid_ptr, uint8_t num_cascades) { uint8_t resp[3] = { 0 }; // theoretically. max 1 Byte SAK, 2 Byte CRC, 3 bytes is enough uint8_t resp_par[1] = {0}; @@ -3524,17 +3660,23 @@ OUT: // Therefore try in alternating directions. static int32_t dist_nt(uint32_t nt1, uint32_t nt2) { - if (nt1 == nt2) return 0; + if (nt1 == nt2) { + return 0; + } uint32_t nttmp1 = nt1; uint32_t nttmp2 = nt2; for (uint16_t i = 1; i < 32768; i++) { nttmp1 = prng_successor(nttmp1, 1); - if (nttmp1 == nt2) return i; + if (nttmp1 == nt2) { + return i; + } nttmp2 = prng_successor(nttmp2, 1); - if (nttmp2 == nt1) return -i; + if (nttmp2 == nt1) { + return -i; + } } return (-99999); // either nt1 or nt2 are invalid nonces @@ -3542,8 +3684,8 @@ static int32_t dist_nt(uint32_t nt1, uint32_t nt2) { #define PRNG_SEQUENCE_LENGTH (1 << 16) -#define MAX_UNEXPECTED_RANDOM 4 // maximum number of unexpected (i.e. real) random numbers when trying to sync. Then give up. -#define MAX_SYNC_TRIES 32 +#define MAX_UNEXPECTED_RANDOM (4) // maximum number of unexpected (i.e. real) random numbers when trying to sync. Then give up. +#define MAX_SYNC_TRIES (32) //----------------------------------------------------------------------------- // Recover several bits of the cypher stream. This implements (first stages of) @@ -3697,9 +3839,9 @@ void ReaderMifare(bool first_try, uint8_t block, uint8_t keytype) { // we didn't calibrate our clock yet, // iceman: has to be calibrated every time. - if (previous_nt && !nt_attacked) { + if (previous_nt && (nt_attacked == 0)) { - int nt_distance = dist_nt(previous_nt, nt); + int32_t nt_distance = dist_nt(previous_nt, nt); // if no distance between, then we are in sync. if (nt_distance == 0) { @@ -3725,7 +3867,9 @@ void ReaderMifare(bool first_try, uint8_t block, uint8_t keytype) { sync_cycles = (sync_cycles - nt_distance) / elapsed_prng_sequences; // no negative sync_cycles, and too small sync_cycles will result in continuous misses - if (sync_cycles <= 10) sync_cycles += PRNG_SEQUENCE_LENGTH; + if (sync_cycles <= 10) { + sync_cycles += PRNG_SEQUENCE_LENGTH; + } // reset sync_cycles if (sync_cycles > PRNG_SEQUENCE_LENGTH * 2) { @@ -3733,8 +3877,14 @@ void ReaderMifare(bool first_try, uint8_t block, uint8_t keytype) { sync_time = GetCountSspClk() & 0xfffffff8; } - if (g_dbglevel >= DBG_EXTENDED) - Dbprintf("calibrating in cycle %d. nt_distance=%d, elapsed_prng_sequences=%d, new sync_cycles: %d\n", i, nt_distance, elapsed_prng_sequences, sync_cycles); + if (g_dbglevel >= DBG_EXTENDED) { + Dbprintf("calibrating in cycle %d. nt_distance=%d, elapsed_prng_sequences=%d, new sync_cycles: %d\n" + , i + , nt_distance + , elapsed_prng_sequences + , sync_cycles + ); + } continue; } @@ -3764,8 +3914,9 @@ void ReaderMifare(bool first_try, uint8_t block, uint8_t keytype) { } else { sync_cycles += catch_up_cycles; - if (g_dbglevel >= DBG_EXTENDED) + if (g_dbglevel >= DBG_EXTENDED) { Dbprintf("Lost sync in cycle %d for the fourth time consecutively (nt_distance = %d). Adjusting sync_cycles to %d.\n", i, catch_up_cycles, sync_cycles); + } last_catch_up = 0; catch_up_cycles = 0; @@ -3778,8 +3929,9 @@ void ReaderMifare(bool first_try, uint8_t block, uint8_t keytype) { if (received_nack) { catch_up_cycles = 8; // the PRNG is delayed by 8 cycles due to the NAC (4Bits = 0x05 encrypted) transfer - if (nt_diff == 0) + if (nt_diff == 0) { par_low = par[0] & 0xE0; // there is no need to check all parities for other nt_diff. Parity Bits for mf_nr_ar[0..2] won't change + } par_list[nt_diff] = reflect8(par[0]); ks_list[nt_diff] = receivedAnswer[0] ^ 0x05; // xor with NACK value to get keystream @@ -3798,12 +3950,15 @@ void ReaderMifare(bool first_try, uint8_t block, uint8_t keytype) { } else { // No NACK. if (nt_diff == 0 && first_try) { + par[0]++; + if (par[0] == 0) { // tried all 256 possible parities without success. Card doesn't send NACK. isOK = 2; return_status = PM3_ESOFT; break; } + } else { // Why this? par[0] = ((par[0] & 0x1F) + 1) | par_low; @@ -3854,7 +4009,7 @@ void DetectNACKbug(void) { uint8_t uid[10] = { 0x00 }; uint8_t receivedAnswer[MAX_MIFARE_FRAME_SIZE] = { 0x00 }; uint8_t receivedAnswerPar[MAX_MIFARE_PARITY_SIZE] = { 0x00 }; - uint8_t par[1] = {0x00 }; // maximum 8 Bytes to be sent here, 1 byte parity is therefore enough + uint8_t par[2] = {0x00 }; // maximum 8 Bytes to be sent here, 1 byte parity is therefore enough uint32_t nt = 0, previous_nt = 0, nt_attacked = 0, cuid = 0; int32_t catch_up_cycles = 0, last_catch_up = 0; @@ -3905,9 +4060,9 @@ void DetectNACKbug(void) { ++checkbtn_cnt; // this part is from Piwi's faster nonce collecting part in Hardnested. - if (!have_uid) { // need a full select cycle to get the uid first + if (have_uid == false) { // need a full select cycle to get the uid first iso14a_card_select_t card_info; - if (!iso14443a_select_card(uid, &card_info, &cuid, true, 0, true)) { + if (iso14443a_select_card(uid, &card_info, &cuid, true, 0, true) == 0) { if (g_dbglevel >= DBG_INFO) Dbprintf("Mifare: Can't select card (ALL)"); i = 0; continue; @@ -3929,7 +4084,7 @@ void DetectNACKbug(void) { } have_uid = true; } else { // no need for anticollision. We can directly select the card - if (!iso14443a_fast_select_card(uid, cascade_levels)) { + if (iso14443a_fast_select_card(uid, cascade_levels) == 0) { if (g_dbglevel >= DBG_INFO) Dbprintf("Mifare: Can't select card (UID)"); i = 0; have_uid = false; @@ -4143,7 +4298,7 @@ void SimulateIso14443aTagAID(uint8_t tagType, uint16_t flags, uint8_t *uid, .modulation_n = 0 }; - if (SimulateIso14443aInit(tagType, flags, uid, ats, ats_len, &responses, &cuid, &pages) == false) { + if (SimulateIso14443aInit(tagType, flags, uid, ats, ats_len, &responses, &cuid, &pages, NULL) == false) { BigBuf_free_keep_EM(); reply_ng(CMD_HF_MIFARE_SIMULATE, PM3_EINIT, NULL, 0); return; diff --git a/armsrc/iso14443a.h b/armsrc/iso14443a.h index 420d6e0c0..d3a73bd6a 100644 --- a/armsrc/iso14443a.h +++ b/armsrc/iso14443a.h @@ -143,7 +143,7 @@ RAMFUNC int ManchesterDecoding(uint8_t bit, uint16_t offset, uint32_t non_real_t void RAMFUNC SniffIso14443a(uint8_t param); void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *useruid, uint8_t exitAfterNReads, - uint8_t *ats, size_t ats_len); + uint8_t *ats, size_t ats_len, bool ulc_part1, bool ulc_part2); 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, @@ -152,7 +152,8 @@ void SimulateIso14443aTagAID(uint8_t tagType, uint16_t flags, uint8_t *uid, bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data, uint8_t *ats, size_t ats_len, tag_response_info_t **responses, - uint32_t *cuid, uint8_t *pages); + uint32_t *cuid, uint8_t *pages, + uint8_t *ulc_key); bool GetIso14443aCommandFromReader(uint8_t *received, uint16_t received_maxlen, uint8_t *par, int *len); void iso14443a_antifuzz(uint32_t flags); @@ -165,8 +166,10 @@ uint16_t ReaderReceive(uint8_t *receivedAnswer, uint16_t answer_maxlen, uint8_t void iso14443a_setup(uint8_t fpga_minor_mode); int iso14_apdu(uint8_t *cmd, uint16_t cmd_len, bool send_chaining, void *data, uint16_t data_len, uint8_t *res); int iso14443a_select_card(uint8_t *uid_ptr, iso14a_card_select_t *p_card, uint32_t *cuid_ptr, bool anticollision, uint8_t num_cascades, bool no_rats); -int iso14443a_select_cardEx(uint8_t *uid_ptr, iso14a_card_select_t *p_card, uint32_t *cuid_ptr, bool anticollision, uint8_t num_cascades, bool no_rats, iso14a_polling_parameters_t *polling_parameters); -int iso14443a_fast_select_card(uint8_t *uid_ptr, uint8_t num_cascades); +int iso14443a_select_cardEx(uint8_t *uid_ptr, iso14a_card_select_t *p_card, uint32_t *cuid_ptr, + bool anticollision, uint8_t num_cascades, bool no_rats, + const iso14a_polling_parameters_t *polling_parameters); +int iso14443a_fast_select_card(const uint8_t *uid_ptr, uint8_t num_cascades); void iso14a_set_trigger(bool enable); int EmSendCmd14443aRaw(const uint8_t *resp, uint16_t respLen); @@ -181,8 +184,9 @@ int EmSendPrecompiledCmd(tag_response_info_t *p_response); bool prepare_allocated_tag_modulation(tag_response_info_t *response_info, uint8_t **buffer, size_t *max_buffer_size); bool prepare_tag_modulation(tag_response_info_t *response_info, size_t max_buffer_size); -bool EmLogTrace(uint8_t *reader_data, uint16_t reader_len, uint32_t reader_StartTime, uint32_t reader_EndTime, uint8_t *reader_Parity, - uint8_t *tag_data, uint16_t tag_len, uint32_t tag_StartTime, uint32_t tag_EndTime, uint8_t *tag_Parity); +bool EmLogTrace(const uint8_t *reader_data, uint16_t reader_len, uint32_t reader_StartTime, + uint32_t reader_EndTime, const uint8_t *reader_Parity, const uint8_t *tag_data, + uint16_t tag_len, uint32_t tag_StartTime, uint32_t tag_EndTime, const uint8_t *tag_Parity); void ReaderMifare(bool first_try, uint8_t block, uint8_t keytype); void DetectNACKbug(void); diff --git a/armsrc/mifarecmd.c b/armsrc/mifarecmd.c index a84ad0096..4386f5003 100644 --- a/armsrc/mifarecmd.c +++ b/armsrc/mifarecmd.c @@ -344,7 +344,7 @@ void MifareUReadBlock(uint8_t arg0, uint8_t arg1, uint8_t *datain) { uint8_t key[16] = {0x00}; memcpy(key, datain, sizeof(key)); - if (!mifare_ultra_auth(key)) { + if (mifare_ultra_auth(key) == 0) { OnError(1); return; } @@ -1947,7 +1947,7 @@ void MifareChkKeys_fast(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *da // Now append the SPI flash dictionnary if (SPIFFS_OK == rdv40_spiffs_read_as_filetype(MF_KEYS_FILE, dictkeys + (keyCount * MF_KEY_LENGTH), (key_mem_available - keyCount) * MF_KEY_LENGTH, RDV40_SPIFFS_SAFETY_SAFE)) { if (g_dbglevel >= DBG_ERROR) { - Dbprintf("loaded " _GREEN_("%u") " keys from spiffs file `" _YELLOW_("%s") "`", key_mem_available, MF_KEYS_FILE); + Dbprintf("loaded " _GREEN_("%u") " keys from spiffs file `" _YELLOW_("%s") "`", key_mem_available - keyCount, MF_KEYS_FILE); } } else { Dbprintf("Spiffs file `" _RED_("%s") "` cannot be read", MF_KEYS_FILE); @@ -3561,7 +3561,7 @@ void MifareGen3Blk(uint8_t block_len, uint8_t *block) { AddCrc14A(cmd, sizeof(block_cmd) + MIFARE_BLOCK_SIZE); if (doReselect) { - if (!iso14443a_select_card(NULL, NULL, NULL, true, 0, true)) { + if (iso14443a_select_card(NULL, NULL, NULL, true, 0, true) == 0) { retval = PM3_ESOFT; goto OUT; } diff --git a/armsrc/mifaredesfire.c b/armsrc/mifaredesfire.c index 448f475dd..cc2996b23 100644 --- a/armsrc/mifaredesfire.c +++ b/armsrc/mifaredesfire.c @@ -60,7 +60,7 @@ bool InitDesfireCard(void) { iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); set_tracing(true); - if (!iso14443a_select_card(NULL, &card, NULL, true, 0, false)) { + if (iso14443a_select_card(NULL, &card, NULL, true, 0, false) == 0) { if (g_dbglevel >= DBG_ERROR) DbpString("Can't select card"); OnError(1); return false; @@ -157,7 +157,7 @@ void MifareDesfireGetInformation(void) { pcb_blocknum = 0; // card select - information - if (!iso14443a_select_card(NULL, &card, NULL, true, 0, false)) { + if (iso14443a_select_card(NULL, &card, NULL, true, 0, false) == 0) { if (g_dbglevel >= DBG_ERROR) { DbpString("Can't select card"); } diff --git a/armsrc/sam_picopass.c b/armsrc/sam_picopass.c index fe9393283..fa040288f 100644 --- a/armsrc/sam_picopass.c +++ b/armsrc/sam_picopass.c @@ -103,10 +103,13 @@ static int sam_send_request_iso15(const uint8_t *const request, const uint8_t re nfc_tx_len = sam_copy_payload_sam2nfc(nfc_tx_buf, sam_rx_buf); - bool is_cmd_check = (nfc_tx_buf[0] & 0x0F) == ICLASS_CMD_CHECK; + bool is_cmd_check = ((nfc_tx_buf[0] & 0x0F) == ICLASS_CMD_CHECK); + if (is_cmd_check && break_on_nr_mac) { + memcpy(response, nfc_tx_buf, nfc_tx_len); *response_len = nfc_tx_len; + if (g_dbglevel >= DBG_INFO) { DbpString("NR-MAC: "); Dbhexdump((*response_len) - 1, response + 1, false); @@ -115,7 +118,8 @@ static int sam_send_request_iso15(const uint8_t *const request, const uint8_t re goto out; } - bool is_cmd_update = (nfc_tx_buf[0] & 0x0F) == ICLASS_CMD_UPDATE; + bool is_cmd_update = ((nfc_tx_buf[0] & 0x0F) == ICLASS_CMD_UPDATE); + if (is_cmd_update && prevent_epurse_update && nfc_tx_buf[0] == 0x87 && nfc_tx_buf[1] == 0x02) { // block update(2) command and fake the response to prevent update of epurse @@ -223,13 +227,13 @@ static int sam_send_request_iso15(const uint8_t *const request, const uint8_t re // 07 // 90 00 if (request_len == 0) { - if ( - !(sam_rx_buf[5] == 0xbd && sam_rx_buf[5 + 2] == 0x8a && sam_rx_buf[5 + 4] == 0x03) - && - !(sam_rx_buf[5] == 0xbd && sam_rx_buf[5 + 2] == 0xb3 && sam_rx_buf[5 + 4] == 0xa0) - ) { - if (g_dbglevel >= DBG_ERROR) + + if (!(sam_rx_buf[5] == 0xbd && sam_rx_buf[5 + 2] == 0x8a && sam_rx_buf[5 + 4] == 0x03) && + !(sam_rx_buf[5] == 0xbd && sam_rx_buf[5 + 2] == 0xb3 && sam_rx_buf[5 + 4] == 0xa0)) { + + if (g_dbglevel >= DBG_ERROR) { Dbprintf("No PACS data in SAM response"); + } res = PM3_ESOFT; } } @@ -361,14 +365,14 @@ int sam_picopass_get_pacs(PacketCommandNG *c) { goto out; } - if (!skipDetect) { + if (skipDetect == false) { // step 2: get card information picopass_hdr_t card_a_info; uint32_t eof_time = 0; // implicit StartSspClk() happens here Iso15693InitReader(); - if (!select_iclass_tag(&card_a_info, false, &eof_time, shallow_mod)) { + if (select_iclass_tag(&card_a_info, false, &eof_time, shallow_mod) == false) { goto err; } @@ -383,8 +387,10 @@ int sam_picopass_get_pacs(PacketCommandNG *c) { if (res != PM3_SUCCESS) { goto err; } - if (g_dbglevel >= DBG_INFO) + + if (g_dbglevel >= DBG_INFO) { print_result("Response data", sam_response, sam_response_len); + } goto out; diff --git a/client/deps/hardnested/hardnested_bruteforce.c b/client/deps/hardnested/hardnested_bruteforce.c index 655ef9dbb..c5d6aef43 100644 --- a/client/deps/hardnested/hardnested_bruteforce.c +++ b/client/deps/hardnested/hardnested_bruteforce.c @@ -177,14 +177,15 @@ crack_states_thread(void *x) { char progress_text[80]; char keystr[19]; - snprintf(keystr, sizeof(keystr), "%012" PRIX64 " ", key); + snprintf(keystr, sizeof(keystr), "%012" PRIX64, key); snprintf(progress_text, sizeof(progress_text), "Brute force phase completed. Key found: " _GREEN_("%s"), keystr); hardnested_print_progress(thread_arg->num_acquired_nonces, progress_text, 0.0, 0); + PrintAndLogEx(INFO, "---------+---------+---------------------------------------------------------+-----------------+-------"); break; } else if (keys_found) { break; } else { - if (!thread_arg->silent) { + if (thread_arg->silent == false) { char progress_text[80]; snprintf(progress_text, sizeof(progress_text), "Brute force phase: %6.02f%% ", 100.0 * (float)num_keys_tested / (float)(thread_arg->maximum_states)); float remaining_bruteforce = thread_arg->nonces[thread_arg->best_first_bytes[0]].expected_num_brute_force - (float)num_keys_tested / 2; @@ -337,7 +338,7 @@ bool brute_force_bs(float *bf_rate, statelist_t *candidates, uint32_t cuid, uint bucket_count = 0; for (statelist_t *p = candidates; p != NULL; p = p->next) { if (p->states[ODD_STATE] != NULL && p->states[EVEN_STATE] != NULL) { - if (!ensure_buckets_alloc(bucket_count + 1)) { + if (ensure_buckets_alloc(bucket_count + 1) == false) { PrintAndLogEx(ERR, "Can't allocate buckets, abort!"); return false; } @@ -375,6 +376,7 @@ bool brute_force_bs(float *bf_rate, statelist_t *candidates, uint32_t cuid, uint thread_args[i].best_first_bytes = best_first_bytes; pthread_create(&threads[i], NULL, crack_states_thread, (void *)&thread_args[i]); } + for (uint32_t i = 0; i < num_brute_force_threads; i++) { pthread_join(threads[i], 0); } @@ -385,11 +387,13 @@ bool brute_force_bs(float *bf_rate, statelist_t *candidates, uint32_t cuid, uint uint64_t elapsed_time = msclock() - start_time; - if (bf_rate != NULL) + if (bf_rate != NULL) { *bf_rate = (float)num_keys_tested / ((float)elapsed_time / 1000.0); + } - if (keys_found > 0) + if (keys_found > 0) { *found_key = found_bs_key; + } return (keys_found != 0); } diff --git a/client/pyscripts/fm11rf08s_recovery.py b/client/pyscripts/fm11rf08s_recovery.py index cbb7246d5..5e13b461c 100755 --- a/client/pyscripts/fm11rf08s_recovery.py +++ b/client/pyscripts/fm11rf08s_recovery.py @@ -584,8 +584,6 @@ def recovery(init_check=False, final_check=False, keep=False, no_oob=False, if "Found keys have been dumped to" in line: keyfile = line[line.index("`"):].strip("`") else: - show() - show(color("found keys:", fg="green"), prompt=plus) show(prompt=plus) show("-----+-----+--------------+---+--------------+----", prompt=plus) show(" Sec | Blk | key A |res| key B |res", prompt=plus) diff --git a/client/src/cmdflashmem.c b/client/src/cmdflashmem.c index fa88549f7..607a36b31 100644 --- a/client/src/cmdflashmem.c +++ b/client/src/cmdflashmem.c @@ -192,21 +192,24 @@ static int CmdFlashMemLoad(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "mem load", "Loads binary file into flash memory on device\n" - "Warning: mem area to be written must have been wiped first\n" - "( dictionaries are serviced as files in spiffs so no wipe is needed )", - "mem load -f myfile -> upload file myfile values at default offset 0\n" - "mem load -f myfile -o 1024 -> upload file myfile values at offset 1024\n" - "mem load -f mfc_default_keys -m -> upload MFC keys\n" - "mem load -f t55xx_default_pwds -t -> upload T55XX passwords\n" - "mem load -f iclass_default_keys -i -> upload iCLASS keys\n" + "Warning! - mem area to be written must have been wiped first\n\n" + "OBS! - dictionaries are serviced as files in spiffs so no wipe is needed", + "mem load -f myfile -> upload file myfile values at default offset 0\n" + "mem load -f myfile -o 1024 -> upload file myfile values at offset 1024\n" + "mem load -f mfc_default_keys -m -> upload MIFARE Classic keys\n" + "mem load -f t55xx_default_pwds -t -> upload T55XX passwords\n" + "mem load -f iclass_default_keys -i -> upload iCLASS keys\n" + "mem load -f mfulc_default_keys --ulc -> upload MIFARE UL-C keys\n" ); void *argtable[] = { arg_param_begin, arg_int0("o", "offset", "", "offset in memory"), - arg_lit0("m", "mifare,mfc", "upload 6 bytes keys (mifare key dictionary)"), - arg_lit0("i", "iclass", "upload 8 bytes keys (iClass key dictionary)"), - arg_lit0("t", "t55xx", "upload 4 bytes keys (password dictionary)"), + arg_lit0("m", "mfc", "upload 6 bytes keys (MIFARE Classic dictionary)"), + arg_lit0("i", "iclass", "upload 8 bytes keys (iClass dictionary)"), + arg_lit0("t", "t55xx", "upload 4 bytes keys (T55xx dictionary)"), + arg_lit0(NULL, "ulc", "upload 16 bytes keys (MIFARE UL-C dictionary)"), + arg_lit0(NULL, "aes", "upload 16 bytes keys (MIFARE UL-AES dictionary)"), arg_str1("f", "file", "", "file name"), arg_param_end }; @@ -216,28 +219,35 @@ static int CmdFlashMemLoad(const char *Cmd) { bool is_mfc = arg_get_lit(ctx, 2); bool is_iclass = arg_get_lit(ctx, 3); bool is_t55xx = arg_get_lit(ctx, 4); + bool is_ulc = arg_get_lit(ctx, 5); + bool is_ulaes = arg_get_lit(ctx, 6); int fnlen = 0; char filename[FILE_PATH_SIZE] = {0}; - char spiffsDest[32] = {0}; - CLIParamStrToBuf(arg_get_str(ctx, 5), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); + CLIParamStrToBuf(arg_get_str(ctx, 7), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); CLIParserFree(ctx); Dictionary_t d = DICTIONARY_NONE; if (is_mfc) { d = DICTIONARY_MIFARE; - PrintAndLogEx(INFO, "treating file as MIFARE Classic keys"); + PrintAndLogEx(INFO, "Treating file as MIFARE Classic keys"); } else if (is_iclass) { d = DICTIONARY_ICLASS; - PrintAndLogEx(INFO, "treating file as iCLASS keys"); + PrintAndLogEx(INFO, "Treating file as iCLASS keys"); } else if (is_t55xx) { d = DICTIONARY_T55XX; - PrintAndLogEx(INFO, "treating file as T55xx passwords"); + PrintAndLogEx(INFO, "Treating file as T55xx passwords"); + } else if (is_ulc) { + d = DICTIONARY_MIFARE_ULC; + PrintAndLogEx(INFO, "Treating file as MIFARE Ultralight-C keys"); + } else if (is_ulaes) { + d = DICTIONARY_MIFARE_ULAES; + PrintAndLogEx(INFO, "Treating file as MIFARE Ultralight AES keys"); } uint8_t spi_flash_pages = 0; int res = rdv4_get_flash_pages64k(&spi_flash_pages); if (res != PM3_SUCCESS) { - PrintAndLogEx(ERR, "failed to get flash pages count (%x)", res); + PrintAndLogEx(ERR, "Failed to get flash pages count (%x)", res); return res; } @@ -246,6 +256,8 @@ static int CmdFlashMemLoad(const char *Cmd) { uint8_t keylen = 0; uint8_t *data = calloc(FLASH_MEM_MAX_SIZE_P(spi_flash_pages), sizeof(uint8_t)); + char spiffsDest[32] = {0}; + switch (d) { case DICTIONARY_MIFARE: { keylen = MF_KEY_LENGTH; @@ -292,6 +304,36 @@ static int CmdFlashMemLoad(const char *Cmd) { strcpy(spiffsDest, ICLASS_KEYS_FILE); break; } + case DICTIONARY_MIFARE_ULC: { + keylen = MFULC_KEY_LENGTH; + res = loadFileDICTIONARY(filename, data, &datalen, keylen, &keycount); + if (res || !keycount) { + free(data); + return PM3_EFILE; + } + if (datalen > FLASH_MEM_MAX_SIZE_P(spi_flash_pages)) { + PrintAndLogEx(ERR, "error, filesize is larger than available memory"); + free(data); + return PM3_EOVFLOW; + } + strcpy(spiffsDest, MFULC_KEYS_FILE); + break; + } + case DICTIONARY_MIFARE_ULAES: { + keylen = MFULAES_KEY_LENGTH; + res = loadFileDICTIONARY(filename, data, &datalen, keylen, &keycount); + if (res || !keycount) { + free(data); + return PM3_EFILE; + } + if (datalen > FLASH_MEM_MAX_SIZE_P(spi_flash_pages)) { + PrintAndLogEx(ERR, "error, filesize is larger than available memory"); + free(data); + return PM3_EOVFLOW; + } + strcpy(spiffsDest, MFULAES_KEYS_FILE); + break; + } case DICTIONARY_NONE: { res = loadFile_safe(filename, ".bin", (void **)&data, &datalen); if (res != PM3_SUCCESS) { @@ -330,7 +372,12 @@ static int CmdFlashMemLoad(const char *Cmd) { free(data); return res; } - PrintAndLogEx(SUCCESS, "Wrote "_GREEN_("%u")" passwords to file "_GREEN_("%s"), keycount, spiffsDest); + + if (d == DICTIONARY_T55XX) { + PrintAndLogEx(SUCCESS, "Wrote "_GREEN_("%u")" passwords to file "_GREEN_("%s"), keycount, spiffsDest); + } else { + PrintAndLogEx(SUCCESS, "Wrote "_GREEN_("%u")" keys to file "_GREEN_("%s"), keycount, spiffsDest); + } SendCommandNG(CMD_SPIFFS_UNMOUNT, NULL, 0); SendCommandNG(CMD_SPIFFS_MOUNT, NULL, 0); } else { @@ -729,6 +776,7 @@ static int CmdFlashMemInfo(const char *Cmd) { static command_t CommandTable[] = { {"spiffs", CmdFlashMemSpiFFS, IfPm3Flash, "{ SPI File system }"}, {"help", CmdHelp, AlwaysAvailable, "This help"}, + {"-----------", CmdHelp, IfPm3Flash, "------------------- " _CYAN_("Operations") " -------------------"}, {"baudrate", CmdFlashmemSpiBaud, IfPm3Flash, "Set Flash memory Spi baudrate"}, {"dump", CmdFlashMemDump, IfPm3Flash, "Dump data from flash memory"}, {"info", CmdFlashMemInfo, IfPm3Flash, "Flash memory information"}, diff --git a/client/src/cmdflashmem.h b/client/src/cmdflashmem.h index ad8727204..f47547166 100644 --- a/client/src/cmdflashmem.h +++ b/client/src/cmdflashmem.h @@ -26,7 +26,9 @@ typedef enum { DICTIONARY_NONE = 0, DICTIONARY_MIFARE, DICTIONARY_T55XX, - DICTIONARY_ICLASS + DICTIONARY_ICLASS, + DICTIONARY_MIFARE_ULC, + DICTIONARY_MIFARE_ULAES, } Dictionary_t; int CmdFlashMem(const char *Cmd); diff --git a/client/src/cmdhf.c b/client/src/cmdhf.c index 0cdf49b81..fb9aae5b9 100644 --- a/client/src/cmdhf.c +++ b/client/src/cmdhf.c @@ -477,7 +477,7 @@ int CmdHFSniff(const char *Cmd) { if (kbd_enter_pressed()) { SendCommandNG(CMD_BREAK_LOOP, NULL, 0); - PrintAndLogEx(INFO, "User aborted"); + PrintAndLogEx(WARNING, "\naborted via keyboard!"); break; } diff --git a/client/src/cmdhf14a.c b/client/src/cmdhf14a.c index 9d929b6b7..8d7ec5d5b 100644 --- a/client/src/cmdhf14a.c +++ b/client/src/cmdhf14a.c @@ -880,6 +880,7 @@ int CmdHF14ASim(const char *Cmd) { "hf 14a sim -t 10 -> ST25TA IKEA Rothult\n" "hf 14a sim -t 11 -> Javacard (JCOP)\n" "hf 14a sim -t 12 -> 4K Seos card\n" + "hf 14a sim -t 13 -> MIFARE Ultralight C" ); void *argtable[] = { @@ -890,6 +891,8 @@ int CmdHF14ASim(const char *Cmd) { arg_lit0("x", NULL, "Performs the 'reader attack', nr/ar attack against a reader"), arg_lit0(NULL, "sk", "Fill simulator keys from found keys"), arg_lit0("v", "verbose", "verbose output"), + arg_lit0(NULL, "c1", "UL-C Auth - all zero handshake part 1"), + arg_lit0(NULL, "c2", "UL-C Auth - all zero handshake part 2"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -923,9 +926,12 @@ int CmdHF14ASim(const char *Cmd) { bool setEmulatorMem = arg_get_lit(ctx, 5); bool verbose = arg_get_lit(ctx, 6); + bool ulc_p1 = arg_get_lit(ctx, 7); + bool ulc_p2 = arg_get_lit(ctx, 8); + CLIParserFree(ctx); - if (tagtype > 12) { + if (tagtype > 13) { PrintAndLogEx(ERR, "Undefined tag %d", tagtype); return PM3_EINVARG; } @@ -939,11 +945,16 @@ int CmdHF14ASim(const char *Cmd) { uint16_t flags; uint8_t uid[10]; uint8_t exitAfter; + uint8_t rats[20]; + bool ulc_p1; + bool ulc_p2; } PACKED payload; payload.tagtype = tagtype; payload.flags = flags; payload.exitAfter = exitAfterNReads; + payload.ulc_p1 = ulc_p1; + payload.ulc_p2 = ulc_p2; memcpy(payload.uid, uid, uid_len); clearCommandBuffer(); @@ -1319,7 +1330,7 @@ static int CmdExchangeAPDU(bool chainingin, const uint8_t *datain, int datainlen // Button pressed / user cancelled if (iLen == -3) { - PrintAndLogEx(DEBUG, "ERR: APDU: User aborted"); + PrintAndLogEx(DEBUG, "\naborted via keyboard!"); return PM3_EAPDU_FAIL; } return PM3_SUCCESS; diff --git a/client/src/cmdhffelica.c b/client/src/cmdhffelica.c index d52cb52da..278b301c4 100644 --- a/client/src/cmdhffelica.c +++ b/client/src/cmdhffelica.c @@ -1803,7 +1803,7 @@ static int CmdHFFelicaSniff(const char *Cmd) { for (;;) { if (kbd_enter_pressed()) { SendCommandNG(CMD_BREAK_LOOP, NULL, 0); - PrintAndLogEx(DEBUG, "User aborted"); + PrintAndLogEx(DEBUG, "\naborted via keyboard!"); msleep(300); break; } @@ -1851,7 +1851,7 @@ static int CmdHFFelicaSimLite(const char *Cmd) { for (;;) { if (kbd_enter_pressed()) { SendCommandNG(CMD_BREAK_LOOP, NULL, 0); - PrintAndLogEx(DEBUG, "User aborted"); + PrintAndLogEx(DEBUG, "\naborted via keyboard!"); msleep(300); break; } @@ -2054,7 +2054,7 @@ static int CmdHFFelicaDumpLite(const char *Cmd) { if (kbd_enter_pressed()) { SendCommandNG(CMD_BREAK_LOOP, NULL, 0); - PrintAndLogEx(DEBUG, "User aborted"); + PrintAndLogEx(DEBUG, "\naborted via keyboard!"); return PM3_EOPABORTED; } diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index 0cf89a6d1..035031cf2 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -4609,7 +4609,7 @@ static int iclass_recover(uint8_t key[8], uint32_t index_start, uint32_t loop, u repeat = false; } else if (resp.status == PM3_EOPABORTED) { PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(WARNING, "iCLASS Key Bits Recovery: " _YELLOW_("user aborted")); + PrintAndLogEx(WARNING, "iCLASS Key Bits Recovery: " _YELLOW_("aborted via keyboard!")); repeat = false; } else if (resp.status == PM3_ESOFT) { PrintAndLogEx(NORMAL, ""); @@ -5210,7 +5210,7 @@ static int CmdHFiClassLookUp(const char *Cmd) { item = (iclass_prekey_t *) bsearch(&lookup, prekey, keycount, sizeof(iclass_prekey_t), cmp_uint32); if (item != NULL) { - PrintAndLogEx(SUCCESS, "Found valid key " _GREEN_("%s"), sprint_hex(item->key, 8)); + PrintAndLogEx(SUCCESS, "Found valid key " _GREEN_("%s"), sprint_hex_inrow(item->key, 8)); add_key(item->key); } diff --git a/client/src/cmdhfjooki.c b/client/src/cmdhfjooki.c index 51f61e619..81e7b3546 100644 --- a/client/src/cmdhfjooki.c +++ b/client/src/cmdhfjooki.c @@ -584,7 +584,7 @@ static int CmdHF14AJookiSim(const char *Cmd) { for (;;) { if (kbd_enter_pressed()) { SendCommandNG(CMD_BREAK_LOOP, NULL, 0); - PrintAndLogEx(DEBUG, "User aborted"); + PrintAndLogEx(DEBUG, "\naborted via keyboard!"); break; } diff --git a/client/src/cmdhflegic.c b/client/src/cmdhflegic.c index 0ff062532..be84822bf 100644 --- a/client/src/cmdhflegic.c +++ b/client/src/cmdhflegic.c @@ -560,7 +560,7 @@ static int CmdLegicSim(const char *Cmd) { for (;;) { if (kbd_enter_pressed()) { SendCommandNG(CMD_BREAK_LOOP, NULL, 0); - PrintAndLogEx(DEBUG, "User aborted"); + PrintAndLogEx(DEBUG, "Aborted via keyboard!"); break; } diff --git a/client/src/cmdhfmf.c b/client/src/cmdhfmf.c index 21ccd12ae..269a06fab 100644 --- a/client/src/cmdhfmf.c +++ b/client/src/cmdhfmf.c @@ -63,6 +63,8 @@ typedef struct { const char *level_name; } SaflokKeyLevel; +static int CmdHelp(const char *Cmd); + // Static array for Saflok key levels static const SaflokKeyLevel saflok_key_levels[] = { {1, "Guest Key"}, @@ -218,9 +220,8 @@ static void ParseAndPrintSaflokData(const sector_t *sector0_info, const sector_t uint8_t key_id = decodedBA[1]; // Byte 2 & 3: KeyRecord, including OpeningKey flag - uint8_t key_record_high = decodedBA[2] & 0x7F; uint8_t opening_key = (decodedBA[2] & 0x80) >> 7; - uint16_t key_record = (key_record_high << 8) | decodedBA[3]; + uint16_t key_record = ((decodedBA[2] & 0x3F) << 8) | decodedBA[3]; // Byte 5 & 6: EncryptSequence + Combination uint16_t sequence_combination_number = ((decodedBA[5] & 0x0F) << 8) | decodedBA[6]; @@ -343,10 +344,6 @@ static void ParseAndPrintSaflokData(const sector_t *sector0_info, const sector_t PrintAndLogEx(SUCCESS, "Checksum Valid........ ( %s )", checksum_valid ? _GREEN_("ok") : _RED_("fail")); } - - -static int CmdHelp(const char *Cmd); - /* static int usage_hf14_keybrute(void) { PrintAndLogEx(NORMAL, "J_Run's 2nd phase of multiple sector nested authentication key recovery"); @@ -1248,13 +1245,15 @@ static int CmdHF14AMfDarkside(const char *Cmd) { uint64_t key = 0; uint64_t t1 = msclock(); - int ret = mf_dark_side(blockno, key_type, &key); + int res = mf_dark_side(blockno, key_type, &key); t1 = msclock() - t1; - if (ret != PM3_SUCCESS) return ret; + if (res != PM3_SUCCESS) { + return res; + } - PrintAndLogEx(SUCCESS, "found valid key: " _GREEN_("%012" PRIx64), key); - PrintAndLogEx(SUCCESS, "time in darkside " _YELLOW_("%.0f") " seconds\n", (float)t1 / 1000.0); + PrintAndLogEx(SUCCESS, "Found valid key [ "_GREEN_("%012" PRIX64) " ]", key); + PrintAndLogEx(SUCCESS, "Time in darkside " _YELLOW_("%.0f") " seconds\n", (float)t1 / 1000.0); return PM3_SUCCESS; } @@ -2008,7 +2007,7 @@ static int CmdHF14AMfNested(const char *Cmd) { //TODO: single mode broken? can't CLIExecWithReturn(ctx, Cmd, argtable, false); int keylen = 0; - uint8_t key[6] = {0}; + uint8_t key[MIFARE_KEY_SIZE] = {0}; CLIGetHexWithReturn(ctx, 1, key, &keylen); bool m0 = arg_get_lit(ctx, 2); @@ -2027,6 +2026,7 @@ static int CmdHF14AMfNested(const char *Cmd) { //TODO: single mode broken? can't } else if (arg_get_lit(ctx, 8)) { keyType = MF_KEY_B; } + uint8_t prev_keytype = keyType; keyType = arg_get_int_def(ctx, 9, keyType); if ((arg_get_lit(ctx, 7) || arg_get_lit(ctx, 8)) && (keyType != prev_keytype)) { @@ -2046,13 +2046,16 @@ static int CmdHF14AMfNested(const char *Cmd) { //TODO: single mode broken? can't } else if (arg_get_lit(ctx, 12)) { trgKeyType = MF_KEY_B; } + uint8_t prev_trgkeytype = trgKeyType; trgKeyType = arg_get_int_def(ctx, 13, trgKeyType); + if ((arg_get_lit(ctx, 11) || arg_get_lit(ctx, 12)) && (trgKeyType != prev_trgkeytype)) { CLIParserFree(ctx); PrintAndLogEx(WARNING, "Choose one single target key type"); return PM3_EINVARG; } + bool transferToEml = arg_get_lit(ctx, 14); bool createDumpFile = arg_get_lit(ctx, 15); bool singleSector = trgBlockNo > -1; @@ -2101,16 +2104,16 @@ static int CmdHF14AMfNested(const char *Cmd) { //TODO: single mode broken? can't return PM3_EINVARG; } } + if (SectorsCnt == 1) { SectorsCnt = MIFARE_1K_MAXSECTOR; } - if (keylen != 6) { - PrintAndLogEx(WARNING, "Input key must include 12 HEX symbols"); + if (keylen != MIFARE_KEY_SIZE) { + PrintAndLogEx(WARNING, "Input key must include 6 HEX bytes, got %u", keylen); return PM3_EINVARG; } - sector_t *e_sector = NULL; uint8_t keyBlock[(ARRAYLEN(g_mifare_default_keys) + 1) * 6]; uint64_t key64 = 0; @@ -2124,15 +2127,17 @@ static int CmdHF14AMfNested(const char *Cmd) { //TODO: single mode broken? can't // check if we can authenticate to sector if (mf_check_keys(blockNo, keyType, true, 1, key, &key64) != PM3_SUCCESS) { if (keyType < 2) { - PrintAndLogEx(WARNING, "Wrong key. Can't authenticate to block:%3d key type:%c", blockNo, keyType ? 'B' : 'A'); + PrintAndLogEx(WARNING, "Wrong key. Can't authenticate to block %3d key type %c", blockNo, keyType ? 'B' : 'A'); } else { - PrintAndLogEx(WARNING, "Wrong key. Can't authenticate to block:%3d key type:%02x", blockNo, MIFARE_AUTH_KEYA + keyType); + PrintAndLogEx(WARNING, "Wrong key. Can't authenticate to block %3d key type %02x", blockNo, MIFARE_AUTH_KEYA + keyType); } return PM3_EOPABORTED; } if (singleSector) { - int16_t isOK = mf_nested(blockNo, keyType, key, trgBlockNo, trgKeyType, keyBlock, !ignore_static_encrypted); + + uint8_t foundkey[MIFARE_KEY_SIZE] = {0}; + int16_t isOK = mf_nested(blockNo, keyType, key, trgBlockNo, trgKeyType, foundkey, !ignore_static_encrypted); switch (isOK) { case PM3_ETIMEOUT: PrintAndLogEx(ERR, "command execution time out\n"); @@ -2150,8 +2155,7 @@ static int CmdHF14AMfNested(const char *Cmd) { //TODO: single mode broken? can't PrintAndLogEx(ERR, "Static encrypted nonce detected. Aborted\n"); PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("script run fm11rf08s_recovery.py") "`"); break; - case PM3_SUCCESS: - key64 = bytes_to_num(keyBlock, 6); + case PM3_SUCCESS: { // transfer key to the emulator if (transferToEml) { @@ -2162,32 +2166,43 @@ static int CmdHF14AMfNested(const char *Cmd) { //TODO: single mode broken? can't } else { // 16 block sector sectortrailer = trgBlockNo | 0x0f; } - mf_eml_get_mem(keyBlock, sectortrailer, 1); - if (trgKeyType == MF_KEY_A) - num_to_bytes(key64, 6, keyBlock); - else - num_to_bytes(key64, 6, &keyBlock[10]); + uint8_t block[MFBLOCK_SIZE] = {0}; + mf_eml_get_mem(block, sectortrailer, 1); - mf_elm_set_mem(keyBlock, sectortrailer, 1); - PrintAndLogEx(SUCCESS, "Key transferred to emulator memory."); + if (trgKeyType == MF_KEY_A) { + memcpy(block, foundkey, MIFARE_KEY_SIZE); + } else { + memcpy(block + 10, foundkey, MIFARE_KEY_SIZE); + } + + mf_elm_set_mem(block, sectortrailer, 1); + PrintAndLogEx(SUCCESS, "Key transferred to emulator memory"); } return PM3_SUCCESS; - default : + } + default : { PrintAndLogEx(ERR, "Unknown error\n"); + } } return PM3_SUCCESS; } else { // ------------------------------------ multiple sectors working - uint64_t t1 = msclock(); - e_sector = calloc(SectorsCnt, sizeof(sector_t)); - if (e_sector == NULL) return PM3_EMALLOC; + uint64_t t2; + + sector_t *e_sector = NULL; + if (initSectorTable(&e_sector, SectorsCnt) != PM3_SUCCESS) { + return PM3_EMALLOC; + } // add our known key e_sector[mfSectorNum(blockNo)].foundKey[keyType] = 1; e_sector[mfSectorNum(blockNo)].Key[keyType] = key64; + PrintAndLogEx(SUCCESS, "--- " _CYAN_("Enter dictionary recovery mode") " ---------------"); + PrintAndLogEx(SUCCESS, "Sector count "_YELLOW_("%d"), SectorsCnt); + //test current key and additional standard keys first // add parameter key memcpy(keyBlock + (ARRAYLEN(g_mifare_default_keys) * 6), key, 6); @@ -2196,6 +2211,8 @@ static int CmdHF14AMfNested(const char *Cmd) { //TODO: single mode broken? can't num_to_bytes(g_mifare_default_keys[cnt], 6, (uint8_t *)(keyBlock + cnt * 6)); } + uint64_t t1 = msclock(); + PrintAndLogEx(SUCCESS, "Testing known keys. Sector count "_YELLOW_("%d"), SectorsCnt); int res = mf_check_keys_fast(SectorsCnt, true, true, 1, ARRAYLEN(g_mifare_default_keys) + 1, keyBlock, e_sector, use_flashmemory, false); if (res == PM3_SUCCESS) { @@ -2203,9 +2220,9 @@ static int CmdHF14AMfNested(const char *Cmd) { //TODO: single mode broken? can't goto jumptoend; } - uint64_t t2 = msclock() - t1; - PrintAndLogEx(SUCCESS, "Time to check " _YELLOW_("%zu") " known keys: %.0f seconds\n", ARRAYLEN(g_mifare_default_keys), (float)t2 / 1000.0); - PrintAndLogEx(SUCCESS, "enter nested key recovery"); + t2 = msclock() - t1; + PrintAndLogEx(SUCCESS, "Time in check keys " _YELLOW_("%.0f") " seconds\n", (float)t2 / 1000.0); + PrintAndLogEx(SUCCESS, "--- " _CYAN_("Enter nested key recovery mode") " ---------------"); // nested sectors bool calibrate = !ignore_static_encrypted; @@ -2214,7 +2231,14 @@ static int CmdHF14AMfNested(const char *Cmd) { //TODO: single mode broken? can't for (uint8_t sectorNo = 0; sectorNo < SectorsCnt; ++sectorNo) { for (int i = 0; i < MIFARE_SECTOR_RETRY; i++) { - if (e_sector[sectorNo].foundKey[trgKeyType]) continue; + while (kbd_enter_pressed()) { + PrintAndLogEx(WARNING, "\naborted via keyboard!"); + return PM3_EOPABORTED; + } + + if (e_sector[sectorNo].foundKey[trgKeyType]) { + continue; + } int16_t isOK = mf_nested(blockNo, keyType, key, mfFirstBlockOfSector(sectorNo), trgKeyType, keyBlock, calibrate); switch (isOK) { @@ -2238,7 +2262,7 @@ static int CmdHF14AMfNested(const char *Cmd) { //TODO: single mode broken? can't case PM3_SUCCESS: calibrate = false; e_sector[sectorNo].foundKey[trgKeyType] = 1; - e_sector[sectorNo].Key[trgKeyType] = bytes_to_num(keyBlock, 6); + e_sector[sectorNo].Key[trgKeyType] = bytes_to_num(keyBlock, MIFARE_KEY_SIZE); mf_check_keys_fast(SectorsCnt, true, true, 2, 1, keyBlock, e_sector, false, false); continue; @@ -2252,24 +2276,24 @@ static int CmdHF14AMfNested(const char *Cmd) { //TODO: single mode broken? can't } t1 = msclock() - t1; - PrintAndLogEx(SUCCESS, "time in nested " _YELLOW_("%.0f") " seconds\n", (float)t1 / 1000.0); + PrintAndLogEx(SUCCESS, "Time in nested " _YELLOW_("%.0f") " seconds\n", (float)t1 / 1000.0); // 20160116 If Sector A is found, but not Sector B, try just reading it of the tag? - PrintAndLogEx(INFO, "trying to read key B..."); + PrintAndLogEx(INFO, "Trying to read key B..."); for (int i = 0; i < SectorsCnt; i++) { // KEY A but not KEY B if (e_sector[i].foundKey[0] && !e_sector[i].foundKey[1]) { uint8_t sectrail = (mfFirstBlockOfSector(i) + mfNumBlocksPerSector(i) - 1); - PrintAndLogEx(SUCCESS, "reading block %d", sectrail); + PrintAndLogEx(SUCCESS, "Reading block " _YELLOW_("%d"), sectrail); mf_readblock_t payload; payload.blockno = sectrail; payload.keytype = MF_KEY_A; - num_to_bytes(e_sector[i].Key[0], 6, payload.key); // KEY A + num_to_bytes(e_sector[i].Key[0], MIFARE_KEY_SIZE, payload.key); // KEY A clearCommandBuffer(); SendCommandNG(CMD_HF_MIFARE_READBL, (uint8_t *)&payload, sizeof(mf_readblock_t)); @@ -2284,9 +2308,9 @@ static int CmdHF14AMfNested(const char *Cmd) { //TODO: single mode broken? can't } uint8_t *data = resp.data.asBytes; - key64 = bytes_to_num(data + 10, 6); + key64 = bytes_to_num(data + 10, MIFARE_KEY_SIZE); if (key64) { - PrintAndLogEx(SUCCESS, "data: %s", sprint_hex(data + 10, 6)); + PrintAndLogEx(SUCCESS, "data: %s", sprint_hex(data + 10, MIFARE_KEY_SIZE)); e_sector[i].foundKey[1] = true; e_sector[i].Key[1] = key64; } @@ -2295,10 +2319,6 @@ static int CmdHF14AMfNested(const char *Cmd) { //TODO: single mode broken? can't jumptoend: - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(SUCCESS, _GREEN_("found keys:")); - - //print them printKeyTable(SectorsCnt, e_sector); // transfer them to the emulator @@ -2309,10 +2329,10 @@ jumptoend: mf_eml_get_mem(keyBlock, mfFirstBlockOfSector(i) + mfNumBlocksPerSector(i) - 1, 1); if (e_sector[i].foundKey[0]) - num_to_bytes(e_sector[i].Key[0], 6, keyBlock); + num_to_bytes(e_sector[i].Key[0], MIFARE_KEY_SIZE, keyBlock); if (e_sector[i].foundKey[1]) - num_to_bytes(e_sector[i].Key[1], 6, &keyBlock[10]); + num_to_bytes(e_sector[i].Key[1], MIFARE_KEY_SIZE, &keyBlock[10]); if (i == SectorsCnt - 1) { // Disable fast mode on last packet @@ -2336,6 +2356,9 @@ jumptoend: } free(e_sector); } + + PrintAndLogEx(INFO, "Done!"); + PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; } @@ -2470,8 +2493,8 @@ static int CmdHF14AMfNestedStatic(const char *Cmd) { } uint64_t t2 = msclock() - t1; - PrintAndLogEx(SUCCESS, "Time to check "_YELLOW_("%zu") " known keys: %.0f seconds\n", ARRAYLEN(g_mifare_default_keys), (float)t2 / 1000.0); - PrintAndLogEx(SUCCESS, "enter static nested key recovery"); + PrintAndLogEx(SUCCESS, "Time in check keys " _YELLOW_("%.0f") " seconds\n", (float)t2 / 1000.0); + PrintAndLogEx(SUCCESS, "--- " _CYAN_("Enter static nested key recovery") " --------------"); // nested sectors for (trgKeyType = MF_KEY_A; trgKeyType <= MF_KEY_B; ++trgKeyType) { @@ -2511,7 +2534,7 @@ static int CmdHF14AMfNestedStatic(const char *Cmd) { // 20160116 If Sector A is found, but not Sector B, try just reading it of the tag? - PrintAndLogEx(INFO, "trying to read key B..."); + PrintAndLogEx(INFO, "Trying to read key B..."); for (int i = 0; i < SectorsCnt; i++) { // KEY A but not KEY B if (e_sector[i].foundKey[0] && !e_sector[i].foundKey[1]) { @@ -2548,9 +2571,6 @@ static int CmdHF14AMfNestedStatic(const char *Cmd) { jumptoend: - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(SUCCESS, _GREEN_("found keys:")); - //print them printKeyTable(SectorsCnt, e_sector); @@ -2731,9 +2751,15 @@ static int CmdHF14AMfNestedHard(const char *Cmd) { SetSIMDInstr(SIMD_NEON); #endif - if (in) + if (in) { SetSIMDInstr(SIMD_NONE); + } + // santiy checks + if ((g_session.pm3_present == false) && (tests == false)) { + PrintAndLogEx(INFO, "No device connected"); + return PM3_EFAILED; + } bool known_target_key = (trg_keylen); @@ -2789,13 +2815,13 @@ static int CmdHF14AMfNestedHard(const char *Cmd) { } } - PrintAndLogEx(INFO, "Target block no " _YELLOW_("%3d") ", target key type: " _YELLOW_("%c") ", known target key: " _YELLOW_("%02x%02x%02x%02x%02x%02x%s"), + PrintAndLogEx(INFO, "Target block no " _YELLOW_("%3d") " target key type: " _YELLOW_("%c") " known target key: " _YELLOW_("%02x%02x%02x%02x%02x%02x%s"), trg_blockno, (trg_keytype == MF_KEY_B) ? 'B' : 'A', trg_key[0], trg_key[1], trg_key[2], trg_key[3], trg_key[4], trg_key[5], known_target_key ? "" : " (not set)" ); - PrintAndLogEx(INFO, "File action: " _YELLOW_("%s") ", Slow: " _YELLOW_("%s") ", Tests: " _YELLOW_("%d"), + PrintAndLogEx(INFO, "File action: " _YELLOW_("%s") " Slow: " _YELLOW_("%s") " Tests: " _YELLOW_("%d"), nonce_file_write ? "write" : nonce_file_read ? "read" : "none", slow ? "Yes" : "No", tests); @@ -3010,7 +3036,6 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { int prng_type = PM3_EUNDEF; int isOK = 0; - // ------------------------------ PrintAndLogEx(NORMAL, ""); uint64_t tagT = GetHF14AMfU_Type(); @@ -3139,6 +3164,19 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { block_cnt += 8; } + // check if we can authenticate to sector + uint8_t loopupblk = mfFirstBlockOfSector(sectorno); + if (mf_check_keys(loopupblk, keytype, true, (in_keys_len / MIFARE_KEY_SIZE), in_keys, &key64) != PM3_SUCCESS) { + if (keytype < 2) { + PrintAndLogEx(WARNING, "Known key failed. Can't authenticate to block %3d key type %c", loopupblk, keytype ? 'B' : 'A'); + } else { + PrintAndLogEx(WARNING, "Known key failed. Can't authenticate to block %3d key type %02x", loopupblk, MIFARE_AUTH_KEYA + keytype); + } + known_key = false; + } else { + num_to_bytes(key64, MIFARE_KEY_SIZE, key); + } + // create/initialize key storage structure sector_t *e_sector = NULL; size_t e_sector_cnt = (sector_cnt > sectorno) ? sector_cnt : sectorno + 1; @@ -3172,7 +3210,9 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { // card prng type (weak=1 / hard=0 / select/card comm error = negative value) if (has_staticnonce == NONCE_NORMAL) { + prng_type = detect_classic_prng(); + if (prng_type < 0) { PrintAndLogEx(FAILED, "\nNo tag detected or other tag communication error (%i)", prng_type); free(e_sector); @@ -3180,47 +3220,50 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { return PM3_ESOFT; } - // - has_staticnonce = detect_classic_static_encrypted_nonce(0, MF_KEY_A, g_mifare_default_key); + if (known_key) { + has_staticnonce = detect_classic_static_encrypted_nonce(loopupblk, keytype, key); + } else { + has_staticnonce = detect_classic_static_encrypted_nonce(0, MF_KEY_A, g_mifare_default_key); + } } // print parameters if (verbose) { - PrintAndLogEx(INFO, "======================= " _YELLOW_("SETTINGS") " ======================="); - PrintAndLogEx(INFO, " card sectors .. " _YELLOW_("%d"), sector_cnt); - PrintAndLogEx(INFO, " key supplied .. " _YELLOW_("%s"), known_key ? "True" : "False"); - PrintAndLogEx(INFO, " known sector .. " _YELLOW_("%d"), sectorno); - PrintAndLogEx(INFO, " keytype ....... " _YELLOW_("%c"), (keytype == MF_KEY_B) ? 'B' : 'A'); - PrintAndLogEx(INFO, " known key ..... " _YELLOW_("%s"), sprint_hex_inrow(key, sizeof(key))); + PrintAndLogEx(INFO, "---- " _CYAN_("Command settings") " ------------------------------------------"); + PrintAndLogEx(INFO, "Card sectors... " _YELLOW_("%d"), sector_cnt); + PrintAndLogEx(INFO, "Key supplied... " _YELLOW_("%s"), known_key ? "yes" : "no"); + PrintAndLogEx(INFO, "Known sector... " _YELLOW_("%d"), sectorno); + PrintAndLogEx(INFO, "Key type....... " _YELLOW_("%c"), (keytype == MF_KEY_B) ? 'B' : 'A'); + PrintAndLogEx(INFO, "Known key...... " _YELLOW_("%s"), sprint_hex_inrow(key, sizeof(key))); switch (has_staticnonce) { case NONCE_STATIC: { - PrintAndLogEx(INFO, " card PRNG ..... " _YELLOW_("STATIC")); + PrintAndLogEx(INFO, "Card PRNG ..... " _YELLOW_("STATIC")); break; } case NONCE_STATIC_ENC: { - PrintAndLogEx(INFO, " card PRNG ..... " _YELLOW_("STATIC ENCRYPTED")); + PrintAndLogEx(INFO, "Card PRNG ..... " _RED_("STATIC ENCRYPTED")); break; } case NONCE_NORMAL: { - PrintAndLogEx(INFO, " card PRNG ..... " _YELLOW_("%s"), prng_type ? "WEAK" : "HARD"); + PrintAndLogEx(INFO, "Card PRNG ..... %s", (prng_type) ? "weak" : "hard"); break; } default: { - PrintAndLogEx(INFO, " card PRNG ..... " _YELLOW_("Could not determine PRNG,") " " _RED_("read failed.")); + PrintAndLogEx(INFO, "Card PRNG ..... " _YELLOW_("Could not determine PRNG") " ( " _RED_("read failed") " ) %i", has_staticnonce); break; } } - PrintAndLogEx(INFO, " dictionary .... " _YELLOW_("%s"), strlen(filename) ? filename : "NONE"); - PrintAndLogEx(INFO, " legacy mode ... " _YELLOW_("%s"), legacy_mfchk ? "True" : "False"); + PrintAndLogEx(INFO, "Dictionary .... " _YELLOW_("%s"), strlen(filename) ? filename : "n/a"); + PrintAndLogEx(INFO, "Legacy mode ... %s", (legacy_mfchk) ? _YELLOW_("yes") : "no"); - PrintAndLogEx(INFO, "========================================================================"); + PrintAndLogEx(INFO, "----------------------------------------------------------------"); } // check the user supplied key if (known_key == false) { - PrintAndLogEx(WARNING, "no known key was supplied, key recovery might fail"); + PrintAndLogEx(WARNING, "No known key was supplied, key recovery might fail"); } // Start the timer @@ -3231,6 +3274,7 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { if (use_flashmemory) { fnlen = 0; } + int ret = mf_load_keys(&keyBlock, &key_cnt, in_keys, in_keys_len, filename, fnlen, true); if (ret != PM3_SUCCESS) { free(e_sector); @@ -3241,7 +3285,7 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { // Use the dictionary to find sector keys on the card if (verbose) { - PrintAndLogEx(INFO, "======================= " _YELLOW_("START DICTIONARY ATTACK") " ======================="); + PrintAndLogEx(INFO, "--- " _CYAN_("Enter dictionary recovery mode") " -----------------------------"); } if (legacy_mfchk) { @@ -3314,6 +3358,7 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { uint8_t num_found_keys = 0; for (int i = 0; i < sector_cnt; i++) { for (int j = MF_KEY_A; j <= MF_KEY_B; j++) { + if (e_sector[i].foundKey[j] != 1) { continue; } @@ -3329,13 +3374,13 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { known_key = true; sectorno = i; keytype = j; - PrintAndLogEx(SUCCESS, "target sector %3u key type %c -- found valid key [ " _GREEN_("%s") " ] (used for nested / hardnested attack)", + PrintAndLogEx(SUCCESS, "Target sector " _GREEN_("%3u") " key type " _GREEN_("%c") " -- found valid key [ " _GREEN_("%s") " ] (used for nested / hardnested attack)", i, (j == MF_KEY_B) ? 'B' : 'A', sprint_hex_inrow(tmp_key, sizeof(tmp_key)) ); } else { - PrintAndLogEx(SUCCESS, "target sector %3u key type %c -- found valid key [ " _GREEN_("%s") " ]", + PrintAndLogEx(SUCCESS, "Target sector " _GREEN_("%3u") " key type " _GREEN_("%c") " -- found valid key [ " _GREEN_("%s") " ]", i, (j == MF_KEY_B) ? 'B' : 'A', sprint_hex_inrow(tmp_key, sizeof(tmp_key)) @@ -3354,7 +3399,7 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { // Check if the darkside attack can be used if (prng_type && has_staticnonce != NONCE_STATIC) { if (verbose) { - PrintAndLogEx(INFO, "======================= " _YELLOW_("START DARKSIDE ATTACK") " ======================="); + PrintAndLogEx(INFO, "--- " _CYAN_("Enter darkside key recovery mode") " ---------------------------------"); } PrintAndLogEx(NORMAL, ""); @@ -3365,13 +3410,13 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { goto noValidKeyFound; } - PrintAndLogEx(SUCCESS, "Found valid key [ " _GREEN_("%012" PRIx64) " ]\n", key64); + PrintAndLogEx(SUCCESS, "Found valid key [ " _GREEN_("%012" PRIX64) " ]\n", key64); // Store the keys num_to_bytes(key64, MIFARE_KEY_SIZE, key); e_sector[sectorno].Key[keytype] = key64; e_sector[sectorno].foundKey[keytype] = 'S'; - PrintAndLogEx(SUCCESS, "target sector %3u key type %c -- found valid key [ " _GREEN_("%012" PRIx64) " ] (used for nested / hardnested attack)", + PrintAndLogEx(SUCCESS, "Target sector " _GREEN_("%3u") " key type "_GREEN_("%c") " -- found valid key [ " _GREEN_("%012" PRIX64) " ] (used for nested / hardnested attack)", sectorno, (keytype == MF_KEY_B) ? 'B' : 'A', key64 @@ -3410,8 +3455,9 @@ noValidKeyFound: // If the key is already known, just skip it if (e_sector[current_sector_i].foundKey[current_key_type_i] == 0) { - if (has_staticnonce == NONCE_STATIC) + if (has_staticnonce == NONCE_STATIC) { goto tryStaticnested; + } // Try the found keys are reused if (bytes_to_num(tmp_key, MIFARE_KEY_SIZE) != 0) { @@ -3420,14 +3466,15 @@ noValidKeyFound: for (int i = 0; i < sector_cnt; i++) { for (int j = MF_KEY_A; j <= MF_KEY_B; j++) { // Check if the sector key is already broken - if (e_sector[i].foundKey[j]) + if (e_sector[i].foundKey[j]) { continue; + } // Check if the key works if (mf_check_keys(mfFirstBlockOfSector(i), j, true, 1, tmp_key, &key64) == PM3_SUCCESS) { e_sector[i].Key[j] = bytes_to_num(tmp_key, MIFARE_KEY_SIZE); e_sector[i].foundKey[j] = 'R'; - PrintAndLogEx(SUCCESS, "target sector %3u key type %c -- found valid key [ " _GREEN_("%s") " ]", + PrintAndLogEx(SUCCESS, "Target sector " _GREEN_("%3u") " key type " _GREEN_("%c") " -- found valid key [ " _GREEN_("%s") " ]", i, (j == MF_KEY_B) ? 'B' : 'A', sprint_hex_inrow(tmp_key, sizeof(tmp_key)) @@ -3442,7 +3489,7 @@ noValidKeyFound: if (current_key_type_i == MF_KEY_B) { if (e_sector[current_sector_i].foundKey[0] && !e_sector[current_sector_i].foundKey[1]) { if (verbose) { - PrintAndLogEx(INFO, "======================= " _YELLOW_("START READ B KEY ATTACK") " ======================="); + PrintAndLogEx(INFO, "--- " _CYAN_("Enter read B key recovery mode") " -----------------------"); PrintAndLogEx(INFO, "reading B key of sector %3d with key type %c", current_sector_i, (current_key_type_i == MF_KEY_B) ? 'B' : 'A'); @@ -3458,24 +3505,29 @@ noValidKeyFound: clearCommandBuffer(); SendCommandNG(CMD_HF_MIFARE_READBL, (uint8_t *)&payload, sizeof(mf_readblock_t)); - if (WaitForResponseTimeout(CMD_HF_MIFARE_READBL, &resp, 1500) == false) goto skipReadBKey; + if (WaitForResponseTimeout(CMD_HF_MIFARE_READBL, &resp, 1500) == false) { + goto skipReadBKey; + } - if (resp.status != PM3_SUCCESS) goto skipReadBKey; + if (resp.status != PM3_SUCCESS) { + goto skipReadBKey; + } uint8_t *data = resp.data.asBytes; key64 = bytes_to_num(data + 10, MIFARE_KEY_SIZE); + if (key64) { e_sector[current_sector_i].foundKey[current_key_type_i] = 'A'; e_sector[current_sector_i].Key[current_key_type_i] = key64; num_to_bytes(key64, MIFARE_KEY_SIZE, tmp_key); - PrintAndLogEx(SUCCESS, "target sector %3u key type %c -- found valid key [ " _GREEN_("%s") " ]", + PrintAndLogEx(SUCCESS, "Target sector " _GREEN_("%3u") " key type " _GREEN_("%c") " -- found valid key [ " _GREEN_("%s") " ]", current_sector_i, (current_key_type_i == MF_KEY_B) ? 'B' : 'A', sprint_hex_inrow(tmp_key, sizeof(tmp_key)) ); } else { if (verbose) { - PrintAndLogEx(WARNING, "unknown B key: sector: %3d key type: %c", + PrintAndLogEx(WARNING, "Unknown B key: sector %3d key type %c", current_sector_i, (current_key_type_i == MF_KEY_B) ? 'B' : 'A' ); @@ -3491,14 +3543,17 @@ noValidKeyFound: skipReadBKey: if (e_sector[current_sector_i].foundKey[current_key_type_i] == 0) { - if (has_staticnonce == NONCE_STATIC) + if (has_staticnonce == NONCE_STATIC) { goto tryStaticnested; + } if (prng_type && (nested_failed == false)) { uint8_t retries = 0; + + PrintAndLogEx(NORMAL, ""); if (verbose) { - PrintAndLogEx(INFO, "======================= " _YELLOW_("START NESTED ATTACK") " ======================="); - PrintAndLogEx(INFO, "sector no %3d, target key type %c", + PrintAndLogEx(INFO, "--- " _CYAN_("Enter nested key recovery mode") " -----------------------------"); + PrintAndLogEx(INFO, "Sector " _YELLOW_("%3d") " key type " _YELLOW_("%c"), current_sector_i, (current_key_type_i == MF_KEY_B) ? 'B' : 'A'); } @@ -3544,8 +3599,6 @@ tryNested: e_sector[current_sector_i].Key[current_key_type_i] = 0xffffffffffff; e_sector[current_sector_i].foundKey[current_key_type_i] = false; // Show the results to the user - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(SUCCESS, _GREEN_("found keys:")); printKeyTable(sector_cnt, e_sector); PrintAndLogEx(NORMAL, ""); free(e_sector); @@ -3559,7 +3612,7 @@ tryNested: break; } default: { - PrintAndLogEx(ERR, "unknown Error.\n"); + PrintAndLogEx(ERR, "Unknown error\n"); free(e_sector); free(fptr); return isOK; @@ -3573,8 +3626,6 @@ tryHardnested: // If the nested attack fails then we try the hardnested attack if (isMifarePlus) { // Show the results to the user - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(SUCCESS, _GREEN_("found keys:")); printKeyTable(sector_cnt, e_sector); PrintAndLogEx(NORMAL, ""); free(e_sector); @@ -3582,9 +3633,10 @@ tryHardnested: // If the nested attack fails then we try the hardnested attack return PM3_ESOFT; } + PrintAndLogEx(NORMAL, ""); if (verbose) { - PrintAndLogEx(INFO, "======================= " _YELLOW_("START HARDNESTED ATTACK") " ======================="); - PrintAndLogEx(INFO, "sector no %3d, target key type %c, Slow %s", + PrintAndLogEx(INFO, "--- " _CYAN_("Enter hardnested key recovery mode") " -------------------------"); + PrintAndLogEx(INFO, "Sector " _YELLOW_("%3d") " key type " _YELLOW_("%c") ", slow " _YELLOW_("%s"), current_sector_i, (current_key_type_i == MF_KEY_B) ? 'B' : 'A', slow ? "Yes" : "No"); @@ -3610,8 +3662,6 @@ tryHardnested: // If the nested attack fails then we try the hardnested attack e_sector[current_sector_i].foundKey[current_key_type_i] = false; // Show the results to the user - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(SUCCESS, _GREEN_("found keys:")); printKeyTable(sector_cnt, e_sector); PrintAndLogEx(NORMAL, ""); break; @@ -3637,9 +3687,10 @@ tryHardnested: // If the nested attack fails then we try the hardnested attack if (has_staticnonce == NONCE_STATIC) { tryStaticnested: + PrintAndLogEx(NORMAL, ""); if (verbose) { - PrintAndLogEx(INFO, "======================= " _YELLOW_("START STATIC NESTED ATTACK") " ======================="); - PrintAndLogEx(INFO, "sector no %3d, target key type %c", + PrintAndLogEx(INFO, "--- " _CYAN_("Enter static nested key recovery mode") " -----------------------"); + PrintAndLogEx(INFO, "Sector " _YELLOW_("%3d") ", key type " _YELLOW_("%c"), current_sector_i, (current_key_type_i == MF_KEY_B) ? 'B' : 'A'); } @@ -3672,7 +3723,7 @@ tryStaticnested: // Check if the key was found if (e_sector[current_sector_i].foundKey[current_key_type_i]) { - PrintAndLogEx(SUCCESS, "target sector %3u key type %c -- found valid key [ " _GREEN_("%s") " ]", + PrintAndLogEx(SUCCESS, "Target sector " _GREEN_("%3u") " key type " _GREEN_("%c") " -- found valid key [ " _GREEN_("%s") " ]", current_sector_i, (current_key_type_i == MF_KEY_B) ? 'B' : 'A', sprint_hex_inrow(tmp_key, sizeof(tmp_key)) @@ -3686,9 +3737,6 @@ tryStaticnested: all_found: // Show the results to the user - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(SUCCESS, _GREEN_("found keys:")); - printKeyTable(sector_cnt, e_sector); if (no_save == false) { @@ -3704,21 +3752,26 @@ all_found: clearCommandBuffer(); SendCommandNG(CMD_HF_MIFARE_EML_MEMCLR, NULL, 0); - PrintAndLogEx(INFO, "transferring keys to simulator memory " NOLF); + PrintAndLogEx(INFO, "Transferring keys to simulator memory " NOLF); bool transfer_status = true; for (current_sector_i = 0; current_sector_i < sector_cnt; current_sector_i++) { + mf_eml_get_mem(block, current_sector_i, 1); - if (e_sector[current_sector_i].foundKey[0]) + + if (e_sector[current_sector_i].foundKey[0]) { num_to_bytes(e_sector[current_sector_i].Key[0], MIFARE_KEY_SIZE, block); - if (e_sector[current_sector_i].foundKey[1]) + } + + if (e_sector[current_sector_i].foundKey[1]) { num_to_bytes(e_sector[current_sector_i].Key[1], MIFARE_KEY_SIZE, block + 10); + } transfer_status |= mf_elm_set_mem(block, mfFirstBlockOfSector(current_sector_i) + mfNumBlocksPerSector(current_sector_i) - 1, 1); } PrintAndLogEx(NORMAL, "( %s )", (transfer_status) ? _GREEN_("ok") : _RED_("fail")); - PrintAndLogEx(INFO, "dumping card content to emulator memory (Cmd Error: 04 can occur)"); + PrintAndLogEx(INFO, "Dumping card content to emulator memory (Cmd Error: 04 can occur)"); // use ecfill trick FastDumpWithEcFill(sector_cnt); @@ -3754,6 +3807,7 @@ all_found: } else { snprintf(suffix, sizeof(suffix), "-dump"); } + fptr = GenerateFilename("hf-mf-", suffix); if (fptr == NULL) { free(dump); @@ -3771,7 +3825,7 @@ all_found: out: // Generate and show statistics t1 = msclock() - t1; - PrintAndLogEx(INFO, "autopwn execution time: " _YELLOW_("%.0f") " seconds", (float)t1 / 1000.0); + PrintAndLogEx(INFO, "Autopwn execution time: " _YELLOW_("%.0f") " seconds", (float)t1 / 1000.0); DropField(); free(e_sector); @@ -3882,7 +3936,7 @@ static int CmdHF14AMfChk_fast(const char *Cmd) { return PM3_EMALLOC; } - uint32_t chunksize = keycnt > (PM3_CMD_DATA_SIZE / MIFARE_KEY_SIZE) ? (PM3_CMD_DATA_SIZE / MIFARE_KEY_SIZE) : keycnt; + uint32_t chunksize = (keycnt > (PM3_CMD_DATA_SIZE / MIFARE_KEY_SIZE)) ? (PM3_CMD_DATA_SIZE / MIFARE_KEY_SIZE) : keycnt; bool firstChunk = true, lastChunk = false; int i = 0; @@ -3901,30 +3955,26 @@ static int CmdHF14AMfChk_fast(const char *Cmd) { // strategies. 1= deep first on sector 0 AB, 2= width first on all sectors for (uint8_t strategy = 1; strategy < 3; strategy++) { - PrintAndLogEx(INFO, "Running strategy %u", strategy); + PrintAndLogEx(INFO, "Running strategy " _YELLOW_("%u"), strategy); // main keychunk loop for (i = 0; i < keycnt; i += chunksize) { if (kbd_enter_pressed()) { - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(WARNING, "\naborted via keyboard!\n"); - // field is still ON if not on last chunk clearCommandBuffer(); - SendCommandNG(CMD_FPGA_MAJOR_MODE_OFF, NULL, 0); - // TODO: we're missing these cleanups on arm side, not sure if it's important... - // set_tracing(false); - // BigBuf_free(); - // BigBuf_Clear_ext(false); + SendCommandNG(CMD_BREAK_LOOP, NULL, 0); + SendCommandNG(CMD_FPGA_MAJOR_MODE_OFF, NULL, 0); // field is still ON if not on last chunk + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(WARNING, "\naborted via keyboard!"); goto out; } - PrintAndLogEx(INPLACE, "Testing %5i/%5i ( " _YELLOW_("%02.1f%%") " )", i, keycnt, (float)i * 100 / keycnt); + uint32_t size = ((keycnt - i) > chunksize) ? chunksize : keycnt - i; // last chunk? - if (size == keycnt - i) + if (size == keycnt - i) { lastChunk = true; - + } int res = mf_check_keys_fast_ex(sectorsCnt, firstChunk, lastChunk, strategy, size, keyBlock + (i * MIFARE_KEY_SIZE), e_sector, false, false, true, singleSectorParams); if (firstChunk) firstChunk = false; @@ -3934,11 +3984,16 @@ static int CmdHF14AMfChk_fast(const char *Cmd) { PrintAndLogEx(NORMAL, ""); goto out; } + PrintAndLogEx(INPLACE, "Testing %5i/%5i ( " _YELLOW_("%02.1f %%") " )", i, keycnt, (float)i * 100 / keycnt); } // end chunks of keys - PrintAndLogEx(INPLACE, "Testing %5i/%5i ( " _YELLOW_("100.00%%") " )", keycnt, keycnt); + + PrintAndLogEx(INPLACE, "Testing %5i/%5i ( " _YELLOW_("100 %%") " ) ", keycnt, keycnt); PrintAndLogEx(NORMAL, ""); + + // reset chunks when swapping strategies firstChunk = true; lastChunk = false; + if (blockn != -1) { break; } @@ -3967,9 +4022,6 @@ out: PrintAndLogEx(WARNING, "No keys found"); } else { - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(SUCCESS, _GREEN_("found keys:")); - printKeyTable(sectorsCnt, e_sector); if (use_flashmemory && found_keys == (sectorsCnt << 1)) { @@ -4188,9 +4240,6 @@ out: PrintAndLogEx(WARNING, "No keys found"); } else { - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(SUCCESS, _GREEN_("found keys:")); - printKeyTable(sectorsCnt, e_sector); if (transferToEml) { @@ -4355,9 +4404,9 @@ static int CmdHF14AMfChk(const char *Cmd) { uint8_t *keyBlock = NULL; uint32_t keycnt = 0; - int ret = mf_load_keys(&keyBlock, &keycnt, key, keylen, filename, fnlen, load_default); - if (ret != PM3_SUCCESS) { - return ret; + int res = mf_load_keys(&keyBlock, &keycnt, key, keylen, filename, fnlen, load_default); + if (res != PM3_SUCCESS) { + return res; } uint64_t key64 = 0; @@ -4406,7 +4455,8 @@ static int CmdHF14AMfChk(const char *Cmd) { uint32_t size = keycnt - c > max_keys ? max_keys : keycnt - c; - if (mf_check_keys(b, trgKeyType, clearLog, size, &keyBlock[MIFARE_KEY_SIZE * c], &key64) == PM3_SUCCESS) { + res = mf_check_keys(b, trgKeyType, clearLog, size, &keyBlock[MIFARE_KEY_SIZE * c], &key64); + if (res == PM3_SUCCESS) { e_sector[i].Key[trgKeyType] = key64; e_sector[i].foundKey[trgKeyType] = true; clearLog = false; @@ -4414,18 +4464,21 @@ static int CmdHF14AMfChk(const char *Cmd) { } clearLog = false; } - if (singleSector) + + if (singleSector) { break; + } b < 127 ? (b += 4) : (b += 16); } } + t1 = msclock() - t1; - PrintAndLogEx(INFO, "\ntime in checkkeys " _YELLOW_("%.0f") " seconds\n", (float)t1 / 1000.0); + PrintAndLogEx(INFO, "\nTime in checkkeys " _YELLOW_("%.0f") " seconds\n", (float)t1 / 1000.0); // 20160116 If Sector A is found, but not Sector B, try just reading it of the tag? if (keyType != MF_KEY_B) { - PrintAndLogEx(INFO, "testing to read key B..."); + PrintAndLogEx(INFO, "Testing to read key B..."); // loop sectors but block is used as to keep track of from which blocks to test int b = blockNo; @@ -4472,9 +4525,6 @@ static int CmdHF14AMfChk(const char *Cmd) { } out: - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(SUCCESS, _GREEN_("found keys:")); - //print keys // if (singleSector) // printKeyTableEx(1, e_sector, mfSectorNum(blockNo)); @@ -4485,15 +4535,18 @@ out: // fast push mode g_conn.block_after_ACK = true; uint8_t block[MFBLOCK_SIZE] = {0x00}; + for (int i = 0; i < sectors_cnt; ++i) { uint8_t blockno = mfFirstBlockOfSector(i) + mfNumBlocksPerSector(i) - 1; mf_eml_get_mem(block, blockno, 1); - if (e_sector[i].foundKey[0]) + if (e_sector[i].foundKey[0]) { num_to_bytes(e_sector[i].Key[0], MIFARE_KEY_SIZE, block); + } - if (e_sector[i].foundKey[1]) + if (e_sector[i].foundKey[1]) { num_to_bytes(e_sector[i].Key[1], MIFARE_KEY_SIZE, block + 10); + } if (i == sectors_cnt - 1) { // Disable fast mode on last packet @@ -4855,7 +4908,7 @@ static int CmdHF14AMfKeyBrute(const char *Cmd) { uint64_t t1 = msclock(); if (mfKeyBrute(blockNo, keytype, key, &foundkey)) - PrintAndLogEx(SUCCESS, "found valid key: %012" PRIx64 " \n", foundkey); + PrintAndLogEx(SUCCESS, "Found valid key [ %012" PRIX64 " ]\n", foundkey); else PrintAndLogEx(FAILED, "key not found"); @@ -6546,8 +6599,9 @@ static int CmdHf14AMfNack(const char *Cmd) { bool verbose = arg_get_lit(ctx, 1); CLIParserFree(ctx); - if (verbose) + if (verbose) { PrintAndLogEx(INFO, "Started testing card for NACK bug. Press Enter to abort"); + } detect_classic_nackbug(verbose); return PM3_SUCCESS; @@ -10130,7 +10184,7 @@ static int CmdHF14AMfInfo(const char *Cmd) { } if (keylen != 0 && keylen != MIFARE_KEY_SIZE) { - PrintAndLogEx(ERR, "Key length must be %u bytes", MIFARE_KEY_SIZE); + PrintAndLogEx(ERR, "Key length must be %u bytes, got %d", MIFARE_KEY_SIZE, keylen); return PM3_EINVARG; } diff --git a/client/src/cmdhfmfhard.c b/client/src/cmdhfmfhard.c index 8381a4c8d..a04db22ec 100644 --- a/client/src/cmdhfmfhard.c +++ b/client/src/cmdhfmfhard.c @@ -127,7 +127,6 @@ static void print_progress_header(void) { get_SIMD_instruction_set(instr_set); snprintf(progress_text, sizeof(progress_text), "Start using " _YELLOW_("%d") " threads and " _YELLOW_("%s") " SIMD core", num_CPUs(), instr_set); - PrintAndLogEx(INFO, "Hardnested attack starting..."); PrintAndLogEx(INFO, "---------+---------+---------------------------------------------------------+-----------------+-------"); PrintAndLogEx(INFO, " | | | Expected to brute force"); PrintAndLogEx(INFO, " Time | #nonces | Activity | #states | time "); @@ -136,12 +135,16 @@ static void print_progress_header(void) { } void hardnested_print_progress(uint32_t nonces, const char *activity, float brute_force, uint64_t min_diff_print_time) { + static uint64_t last_print_time = 0; + if (msclock() - last_print_time >= min_diff_print_time) { + last_print_time = msclock(); uint64_t total_time = msclock() - start_time; float brute_force_time = brute_force / brute_force_per_second; char brute_force_time_string[20]; + if (brute_force_time < 90) { snprintf(brute_force_time_string, sizeof(brute_force_time_string), "%2.0fs", brute_force_time); } else if (brute_force_time < 60 * 90) { @@ -151,7 +154,24 @@ void hardnested_print_progress(uint32_t nonces, const char *activity, float brut } else { snprintf(brute_force_time_string, sizeof(brute_force_time_string), "%2.0fd", brute_force_time / (60 * 60 * 24)); } - PrintAndLogEx(INFO, " %7.0f | %7u | %-55s | %15.0f | %5s", (float)total_time / 1000.0, nonces, activity, brute_force, brute_force_time_string); + + if (strlen(activity) > 67) { + PrintAndLogEx(INFO, " %7.0f | %7u | %-82s | %15.0f | %5s" + , (float)total_time / 1000.0 + , nonces + , activity + , brute_force + , brute_force_time_string + ); + } else { + PrintAndLogEx(INFO, " %7.0f | %7u | %-55s | %15.0f | %5s" + , (float)total_time / 1000.0 + , nonces + , activity + , brute_force + , brute_force_time_string + ); + } } } @@ -486,8 +506,14 @@ static void init_bitflip_bitarrays(void) { effective_bitflip[odd_even][num_effective_bitflips[odd_even]] = 0x400; // EndOfList marker } { - char progress_text[80]; - snprintf(progress_text, sizeof(progress_text), "Loaded %u RAW / %u LZ4 / %u BZ2 in %"PRIu64" ms", nraw, nlz4, nbz2, msclock() - init_bitflip_bitarrays_starttime); + char progress_text[100]; + memset(progress_text, 0, sizeof(progress_text)); + snprintf(progress_text, sizeof(progress_text), "Loaded " _YELLOW_("%u") " RAW / " _YELLOW_("%u") " LZ4 / " _YELLOW_("%u") " BZ2 in %"PRIu64" ms" + , nraw + , nlz4 + , nbz2 + , msclock() - init_bitflip_bitarrays_starttime + ); hardnested_print_progress(0, progress_text, (float)(1LL << 47), 0); } uint16_t i = 0; @@ -2481,8 +2507,10 @@ int mfnestedhard(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBloc free_candidates_memory(candidates); candidates = NULL; } else { + pre_XOR_nonces(); prepare_bf_test_nonces(nonces, best_first_bytes[0]); + for (uint8_t j = 0; j < NUM_SUMS && !key_found; j++) { float expected_brute_force = nonces[best_first_bytes[0]].expected_num_brute_force; snprintf(progress_text, sizeof(progress_text), "(%d. guess: Sum(a8) = %" PRIu16 ")", j + 1, sums[nonces[best_first_bytes[0]].sum_a8_guess[j].sum_a8_idx]); @@ -2544,7 +2572,9 @@ int mfnestedhard(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBloc int res; if (nonce_file_read) { // use pre-acquired data from file nonces.bin + res = read_nonce_file(filename); + if (res != PM3_SUCCESS) { free_bitflip_bitarrays(); free_nonces_memory(); @@ -2554,12 +2584,16 @@ int mfnestedhard(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBloc free_part_sum_bitarrays(); return res; } + hardnested_stage = CHECK_1ST_BYTES | CHECK_2ND_BYTES; update_nonce_data(false); float brute_force_depth; shrink_key_space(&brute_force_depth); + } else { // acquire nonces. + res = acquire_nonces(blockNo, keyType, key, trgBlockNo, trgKeyType, nonce_file_write, slow, filename); + if (res != PM3_SUCCESS) { free_bitflip_bitarrays(); free_nonces_memory(); diff --git a/client/src/cmdhfmfp.c b/client/src/cmdhfmfp.c index dc5684e43..6433bcf2e 100644 --- a/client/src/cmdhfmfp.c +++ b/client/src/cmdhfmfp.c @@ -1802,9 +1802,6 @@ static int CmdHFMFPChk(const char *Cmd) { t1 = msclock() - t1; PrintAndLogEx(INFO, "\ntime in checkkeys " _YELLOW_("%.0f") " seconds\n", (float)t1 / 1000.0); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(SUCCESS, _GREEN_("found keys:")); - // print result char strA[46 + 1] = {0}; char strB[46 + 1] = {0}; diff --git a/client/src/cmdhfmfu.c b/client/src/cmdhfmfu.c index ccf0de708..1815705de 100644 --- a/client/src/cmdhfmfu.c +++ b/client/src/cmdhfmfu.c @@ -59,11 +59,13 @@ static int CmdHelp(const char *Cmd); +static const char *key_type[] = { "DataProtKey", "UIDRetrKey", "OriginalityKey" }; + static uint8_t default_aes_keys[][16] = { { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // all zeroes { 0x42, 0x52, 0x45, 0x41, 0x4b, 0x4d, 0x45, 0x49, 0x46, 0x59, 0x4f, 0x55, 0x43, 0x41, 0x4e, 0x21 }, // 3des std key - { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f }, // 0x00-0x0F { 0x49, 0x45, 0x4D, 0x4B, 0x41, 0x45, 0x52, 0x42, 0x21, 0x4E, 0x41, 0x43, 0x55, 0x4F, 0x59, 0x46 }, // NFC-key + { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f }, // 0x00-0x0F { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 }, // all ones { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }, // all FF { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF }, // 11 22 33 @@ -74,8 +76,8 @@ static uint8_t default_aes_keys[][16] = { static uint8_t default_3des_keys[][16] = { { 0x42, 0x52, 0x45, 0x41, 0x4b, 0x4d, 0x45, 0x49, 0x46, 0x59, 0x4f, 0x55, 0x43, 0x41, 0x4e, 0x21 }, // 3des std key { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // all zeroes - { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f }, // 0x00-0x0F { 0x49, 0x45, 0x4D, 0x4B, 0x41, 0x45, 0x52, 0x42, 0x21, 0x4E, 0x41, 0x43, 0x55, 0x4F, 0x59, 0x46 }, // NFC-key + { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f }, // 0x00-0x0F { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 }, // all ones { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }, // all FF { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF }, // 11 22 33 @@ -3836,18 +3838,21 @@ static int CmdHF14AMfUSim(const char *Cmd) { "ISO/IEC 14443 type A tag with 4,7 or 10 byte UID\n" "from emulator memory. See `hf mfu eload` first. \n" "The UID from emulator memory will be used if not specified.\n" - "See `hf 14a sim -h` to see available types. You want 2 or 7 usually.", + "See `hf 14a sim -h` to see available types. You want 2, 7 or 13 usually.", "hf mfu sim -t 2 --uid 11223344556677 -> MIFARE Ultralight\n" "hf mfu sim -t 7 --uid 11223344556677 -n 5 -> MFU EV1 / NTAG 215 Amiibo\n" - "hf mfu sim -t 7 -> MFU EV1 / NTAG 215 Amiibo" + "hf mfu sim -t 7 -> MFU EV1 / NTAG 215 Amiibo\n" + "hf mfu sim -t 13 -> MIFARE Ultralight-C\n" ); void *argtable[] = { arg_param_begin, - arg_int1("t", "type", "<1..12> ", "Simulation type to use"), + arg_int1("t", "type", "<1..13> ", "Simulation type to use"), arg_str0("u", "uid", "", "<4|7|10> hex bytes UID"), arg_int0("n", "num", "", "Exit simulation after blocks. 0 = infinite"), arg_lit0("v", "verbose", "Verbose output"), + arg_lit0(NULL, "c1", "UL-C Auth - all zero handshake part 1"), + arg_lit0(NULL, "c2", "UL-C Auth - all zero handshake part 2"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -3966,14 +3971,13 @@ static int CmdHF14AMfUAESAuth(const char *Cmd) { } int result = ulaes_requestAuthentication(authKeyPtr, key_index, !keep_field_on); - - const char *key_type[] = { "DataProtKey", "UIDRetrKey", "OriginalityKey" }; if (result == PM3_SUCCESS) { - PrintAndLogEx(SUCCESS, "Authentication with " _YELLOW_("%s") " " _GREEN_("%s") " ( " _GREEN_("ok")" )", - key_type[key_index], sprint_hex_inrow(authKeyPtr, ak_len)); + PrintAndLogEx(SUCCESS, "Authentication with " _YELLOW_("%s") " " _GREEN_("%s") " ( " _GREEN_("ok")" )" + , key_type[key_index] + , sprint_hex_inrow(authKeyPtr, ak_len) + ); } else { - PrintAndLogEx(WARNING, "Authentication with " _YELLOW_("%s") " ( " _RED_("fail") " )", - key_type[key_index]); + PrintAndLogEx(WARNING, "Authentication with " _YELLOW_("%s") " ( " _RED_("fail") " )", key_type[key_index]); } return result; } diff --git a/client/src/cmdlf.c b/client/src/cmdlf.c index 0282f6c72..20940ae6d 100644 --- a/client/src/cmdlf.c +++ b/client/src/cmdlf.c @@ -83,7 +83,7 @@ int lfsim_wait_check(uint32_t cmd) { for (;;) { if (kbd_enter_pressed()) { SendCommandNG(CMD_BREAK_LOOP, NULL, 0); - PrintAndLogEx(DEBUG, "User aborted"); + PrintAndLogEx(DEBUG, "\naborted via keyboard!"); break; } diff --git a/client/src/cmdlfem4x70.c b/client/src/cmdlfem4x70.c index 75b5da1e7..8fd80e3d7 100644 --- a/client/src/cmdlfem4x70.c +++ b/client/src/cmdlfem4x70.c @@ -587,7 +587,7 @@ static int CmdEM4x70Brute(const char *Cmd) { em4x70_cmd_output_brute_t data; int result = brute_em4x70(&opts, &data); if (result == PM3_EOPABORTED) { - PrintAndLogEx(DEBUG, "User aborted"); + PrintAndLogEx(DEBUG, "\naborted via keyboard!"); } else if (result == PM3_ETIMEOUT) { PrintAndLogEx(WARNING, "\nNo response from Proxmark3. Aborting..."); } else if (result == PM3_SUCCESS) { diff --git a/client/src/cmdlfhitag.c b/client/src/cmdlfhitag.c index 425b0412c..63c25ff99 100644 --- a/client/src/cmdlfhitag.c +++ b/client/src/cmdlfhitag.c @@ -498,7 +498,7 @@ static int ht2_check_dictionary(uint32_t key_count, uint8_t *keys, uint8_t keyl if (kbd_enter_pressed()) { SendCommandNG(CMD_BREAK_LOOP, NULL, 0); - PrintAndLogEx(INFO, "User aborted"); + PrintAndLogEx(WARNING, "\naborted via keyboard!"); break; } diff --git a/client/src/cmdparser.c b/client/src/cmdparser.c index cf6bb1941..6b3984ad4 100644 --- a/client/src/cmdparser.c +++ b/client/src/cmdparser.c @@ -20,7 +20,8 @@ #include #include - +#include // spinlock +#include // system #include "ui.h" #include "comms.h" #include "util_posix.h" // msleep @@ -219,6 +220,28 @@ void CmdsHelp(const command_t Commands[]) { PrintAndLogEx(NORMAL, ""); } +pthread_spinlock_t sycmd_spinlock; + +static int execute_system_command(const char *command) { + + int ret; + + pthread_spin_lock(&sycmd_spinlock); +#if defined(_WIN32) + char wrapped_command[255]; + strncat(wrapped_command, "cmd /C \"", 9); + strncat(wrapped_command, command, strlen(command)); + strncat(wrapped_command, "\"", 2); + + ret = system(wrapped_command); +#else + ret = system(command); +#endif + pthread_spin_unlock(&sycmd_spinlock); + return ret; +} + + int CmdsParse(const command_t Commands[], const char *Cmd) { if (g_session.client_exe_delay != 0) { @@ -267,6 +290,12 @@ int CmdsParse(const command_t Commands[], const char *Cmd) { return PM3_SUCCESS; } + if (Cmd[0] == '!') { + pthread_spin_init(&sycmd_spinlock, 0); + int res = execute_system_command(Cmd + 1); + pthread_spin_destroy(&sycmd_spinlock); + return res; + } char cmd_name[128] = {0}; memset(cmd_name, 0, sizeof(cmd_name)); diff --git a/client/src/emv/emvcore.c b/client/src/emv/emvcore.c index b2aa524ac..b663af706 100644 --- a/client/src/emv/emvcore.c +++ b/client/src/emv/emvcore.c @@ -484,7 +484,7 @@ int EMVSearch(Iso7816CommandChannel channel, bool ActivateField, bool LeaveField for (int i = 0; i < ARRAYLEN(AIDlist); i ++) { if (kbd_enter_pressed()) { - PrintAndLogEx(INFO, "user aborted..."); + PrintAndLogEx(WARNING, "\naborted via keyboard!"); break; } diff --git a/client/src/fileutils.c b/client/src/fileutils.c index aee5e827c..3bea9f3c4 100644 --- a/client/src/fileutils.c +++ b/client/src/fileutils.c @@ -3075,6 +3075,82 @@ int searchFile(char **foundpath, const char *pm3dir, const char *searchname, con return res; } +/** + * Inserts a line into a text file only if it does not already exist. + * Returns PM3_SUCCES or, PM3_EFILE; + * + * @param filepath Path to the file. + * @param line Line to insert (should not contain a trailing newline). + */ +int insert_line_if_not_exists(const char *preferredName, const char *keystr) { + + char *path; + int res = searchFile(&path, DICTIONARIES_SUBDIR, preferredName, ".dic", false); + if (res != PM3_SUCCESS) { + return PM3_EFILE; + } + + FILE *f = fopen(path, "r"); + if (f == NULL) { + PrintAndLogEx(WARNING, "file not found or locked `" _YELLOW_("%s") "`", path); + free(path); + return PM3_EFILE; + } + + // Maximum line length we assume (adjust as necessary for your use case) + char line[255]; + bool key_exists = false; + + char *keystrdup = str_dup(keystr); + str_upper(keystrdup); + + // First pass: check if the line exists + while (fgets(line, sizeof(line), f)) { + + // The line start with # is comment, skip + if (line[0] == '#') { + continue; + } + + // Remove trailing newline for comparison + line[strcspn(line, "\n")] = '\0'; + + // UPPER CASE + str_upper(line); + + key_exists = str_startswith(line, keystrdup); + if (key_exists) { + fclose(f); + free(path); + PrintAndLogEx(INFO, "already in there..."); + return PM3_SUCCESS; + } + } + + fclose(f); + + + // Reopen for appending if line doesn't exist + f = fopen(path, "a"); + if (f == NULL) { + PrintAndLogEx(WARNING, "file not found or locked `" _YELLOW_("%s") "`", path); + free(path); + return PM3_EFILE; + } + + free(path); + + // Append the line with a newline + if (fprintf(f, "%s\n", keystrdup) < 0) { + PrintAndLogEx(WARNING, "error writing to file"); + fclose(f); + return PM3_EFILE; + } + + fclose(f); + return PM3_SUCCESS; +} + int pm3_load_dump(const char *fn, void **pdump, size_t *dumplen, size_t maxdumplen) { int res = PM3_SUCCESS; diff --git a/client/src/fileutils.h b/client/src/fileutils.h index 6fc450dd1..3e8b022c1 100644 --- a/client/src/fileutils.h +++ b/client/src/fileutils.h @@ -385,4 +385,15 @@ int pm3_save_mf_dump(const char *fn, uint8_t *d, size_t n, JSONFileType jsft); * @return PM3_SUCCESS if OK */ int pm3_save_fm11rf08s_nonces(const char *fn, iso14a_fm11rf08s_nonces_with_data_t *d, bool with_data); + + +/** + * Inserts a line into a text file only if it does not already exist. + * Returns PM3_SUCCES or, PM3_EFILE; + * + * @param filepath Path to the file. + * @param line Line to insert (should not contain a trailing newline). + */ +int insert_line_if_not_exists(const char *preferredName, const char *line); + #endif // FILEUTILS_H diff --git a/client/src/loclass/cipherutils.c b/client/src/loclass/cipherutils.c index 83b23c698..2ff42274d 100644 --- a/client/src/loclass/cipherutils.c +++ b/client/src/loclass/cipherutils.c @@ -126,47 +126,67 @@ uint64_t x_bytes_to_num(uint8_t *src, size_t len) { } void printarr(const char *name, uint8_t *arr, int len) { - if (name == NULL || arr == NULL) return; + + if (name == NULL || arr == NULL) { + return; + } int cx, i; size_t outsize = 40 + strlen(name) + len * 5; + char *output = calloc(outsize, sizeof(char)); if (output == NULL) { PrintAndLogEx(WARNING, "Failed to allocate memory"); return; } + cx = snprintf(output, outsize, "uint8_t %s[] = {", name); for (i = 0; i < len; i++) { - if (cx < outsize) + if (cx < outsize) { cx += snprintf(output + cx, outsize - cx, "0x%02x,", *(arr + i)); //5 bytes per byte + } } - if (cx < outsize) + + if (cx < outsize) { snprintf(output + cx, outsize - cx, "};"); + } + PrintAndLogEx(INFO, output); free(output); } void printarr_human_readable(const char *title, uint8_t *arr, int len) { - if (arr == NULL) return; + if (arr == NULL) { + return; + } int cx = 0, i; size_t outsize = 100 + strlen(title) + (len * 4); char *output = calloc(outsize, sizeof(char)); PrintAndLogEx(INFO, "%s", title); + for (i = 0; i < len; i++) { + if (i % 16 == 0) { if (i == 0) { - if (cx < outsize) + + if (cx < outsize) { cx += snprintf(output + cx, outsize - cx, "%02x| ", i); + } + } else { - if (cx < outsize) + + if (cx < outsize) { cx += snprintf(output + cx, outsize - cx, "\n%02x| ", i); + } } } - if (cx < outsize) + + if (cx < outsize) { cx += snprintf(output + cx, outsize - cx, "%02x ", *(arr + i)); + } } PrintAndLogEx(INFO, output); free(output); @@ -233,11 +253,14 @@ static int testReversedBitstream(void) { } int testCipherUtils(void) { - PrintAndLogEx(INFO, "Testing some internals..."); - int retval = testBitStream(); - if (retval == PM3_SUCCESS) - retval = testReversedBitstream(); - return retval; + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "---------------- " _CYAN_("Loclass selftests") " ----------------"); + + int res = testBitStream(); + if (res == PM3_SUCCESS) { + res = testReversedBitstream(); + } + return res; } #endif diff --git a/client/src/loclass/elite_crack.c b/client/src/loclass/elite_crack.c index 01ffe7b29..ad7beb249 100644 --- a/client/src/loclass/elite_crack.c +++ b/client/src/loclass/elite_crack.c @@ -248,10 +248,12 @@ void hash2(uint8_t *key64, uint8_t *outp_keytable) { } if (outp_keytable != NULL) { + for (uint8_t i = 0 ; i < 8 ; i++) { memcpy(outp_keytable + i * 16, y[i], 8); memcpy(outp_keytable + 8 + i * 16, z[i], 8); } + } else { printarr_human_readable("hash2", outp_keytable, 128); } @@ -329,7 +331,9 @@ static void *bf_thread(void *thread_arg) { int found = __atomic_load_n(&loclass_found, __ATOMIC_SEQ_CST); - if (found != 0xFF) return NULL; + if (found != 0xFF) { + return NULL; + } //Update the keytable with the brute-values for (uint8_t i = 0; i < numbytes_to_recover; i++) { @@ -383,15 +387,22 @@ static void *bf_thread(void *thread_arg) { #define _CLR_ "\x1b[0K" if (numbytes_to_recover == 3) { + if ((brute > 0) && ((brute & 0xFFFF) == 0)) { PrintAndLogEx(INPLACE, "[ %02x %02x %02x ] %8u / %u", bytes_to_recover[0], bytes_to_recover[1], bytes_to_recover[2], brute, 0xFFFFFF); } + } else if (numbytes_to_recover == 2) { - if ((brute > 0) && ((brute & 0x3F) == 0)) + + if ((brute > 0) && ((brute & 0x3F) == 0)) { PrintAndLogEx(INPLACE, "[ %02x %02x ] %5u / %u" _CLR_, bytes_to_recover[0], bytes_to_recover[1], brute, 0xFFFF); + } + } else { - if ((brute > 0) && ((brute & 0x1F) == 0)) + + if ((brute > 0) && ((brute & 0x1F) == 0)) { PrintAndLogEx(INPLACE, "[ %02x ] %3u / %u" _CLR_, bytes_to_recover[0], brute, 0xFF); + } } } pthread_exit(NULL); @@ -424,15 +435,19 @@ int bruteforceItem(loclass_dumpdata_t item, uint16_t keytable[]) { uint8_t bytes_to_recover[3] = {0}; uint8_t numbytes_to_recover = 0; for (uint8_t i = 0; i < 8; i++) { - if (keytable[key_index[i]] & (LOCLASS_CRACKED | LOCLASS_BEING_CRACKED)) continue; + + if (keytable[key_index[i]] & (LOCLASS_CRACKED | LOCLASS_BEING_CRACKED)) { + continue; + } bytes_to_recover[numbytes_to_recover++] = key_index[i]; + keytable[key_index[i]] |= LOCLASS_BEING_CRACKED; if (numbytes_to_recover > 3) { PrintAndLogEx(FAILED, "The CSN requires > 3 byte bruteforce, not supported"); - PrintAndLogEx(INFO, "CSN %s", sprint_hex(item.csn, 8)); - PrintAndLogEx(INFO, "HASH1 %s", sprint_hex(key_index, 8)); + PrintAndLogEx(INFO, "CSN..... %s", sprint_hex_inrow(item.csn, 8)); + PrintAndLogEx(INFO, "HASH1... %s", sprint_hex_inrow(key_index, 8)); PrintAndLogEx(NORMAL, ""); //Before we exit, reset the 'BEING_CRACKED' to zero keytable[bytes_to_recover[0]] &= ~LOCLASS_BEING_CRACKED; @@ -443,6 +458,7 @@ int bruteforceItem(loclass_dumpdata_t item, uint16_t keytable[]) { } if (numbytes_to_recover == 0) { + PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "No bytes to recover, exiting"); return PM3_ESOFT; } @@ -472,8 +488,9 @@ int bruteforceItem(loclass_dumpdata_t item, uint16_t keytable[]) { } // wait for threads to terminate: void *ptrs[loclass_tc]; - for (size_t i = 0; i < loclass_tc; i++) + for (size_t i = 0; i < loclass_tc; i++) { pthread_join(threads[i], &ptrs[i]); + } // was it a success? int res = PM3_SUCCESS; @@ -661,7 +678,6 @@ int bruteforceItem(loclass_dumpdata_t item, uint16_t keytable[]) { * @return 0 for ok, 1 for failz */ int calculateMasterKey(uint8_t first16bytes[], uint8_t kcus[]) { - mbedtls_des_context ctx_e; uint8_t z_0[8] = {0}; uint8_t y_0[8] = {0}; @@ -680,8 +696,9 @@ int calculateMasterKey(uint8_t first16bytes[], uint8_t kcus[]) { permutekey_rev(z_0, z_0_rev); // ~K_cus = DESenc(z[0], y[0]) - mbedtls_des_setkey_enc(&ctx_e, z_0_rev); - mbedtls_des_crypt_ecb(&ctx_e, y_0, key64_negated); + mbedtls_des_context ctx; + mbedtls_des_setkey_enc(&ctx, z_0_rev); + mbedtls_des_crypt_ecb(&ctx, y_0, key64_negated); key64[0] = ~key64_negated[0]; key64[1] = ~key64_negated[1]; @@ -697,20 +714,24 @@ int calculateMasterKey(uint8_t first16bytes[], uint8_t kcus[]) { uint8_t key64_stdformat[8] = {0}; permutekey_rev(key64, key64_stdformat); - mbedtls_des_setkey_enc(&ctx_e, key64_stdformat); - mbedtls_des_crypt_ecb(&ctx_e, key64_negated, result); + mbedtls_des_setkey_enc(&ctx, key64_stdformat); + mbedtls_des_crypt_ecb(&ctx, key64_negated, result); + mbedtls_des_free(&ctx); - if (kcus != NULL) + // copy key to out array + if (kcus != NULL) { memcpy(kcus, key64, 8); + } if (memcmp(z_0, result, 4) != 0) { - PrintAndLogEx(WARNING, _RED_("Failed to verify") " calculated master key (k_cus)! Something is wrong."); + PrintAndLogEx(WARNING, "Calculated master key, k_cus ( %s )", _RED_("fail")); + PrintAndLogEx(NORMAL, ""); return PM3_ESOFT; } - PrintAndLogEx(SUCCESS, "----- " _CYAN_("High security custom key (Kcus)") " -----"); - PrintAndLogEx(SUCCESS, "Standard format %s", sprint_hex(key64_stdformat, 8)); - PrintAndLogEx(SUCCESS, "iCLASS format " _GREEN_("%s"), sprint_hex(key64, 8)); + PrintAndLogEx(SUCCESS, "--- " _CYAN_("High security custom key (Kcus)") " ---"); + PrintAndLogEx(SUCCESS, "Standard format... %s", sprint_hex_inrow(key64_stdformat, sizeof(key64_stdformat))); + PrintAndLogEx(SUCCESS, "iCLASS format..... " _GREEN_("%s"), sprint_hex_inrow(key64, sizeof(key64))); PrintAndLogEx(SUCCESS, "Key verified ( " _GREEN_("ok") " )"); PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; @@ -723,7 +744,7 @@ int calculateMasterKey(uint8_t first16bytes[], uint8_t kcus[]) { * @return */ int bruteforceDump(uint8_t dump[], size_t dumpsize, uint16_t keytable[]) { - uint8_t i; + size_t itemsize = sizeof(loclass_dumpdata_t); loclass_dumpdata_t *attack = (loclass_dumpdata_t *) calloc(itemsize, sizeof(uint8_t)); if (attack == NULL) { @@ -737,19 +758,28 @@ int bruteforceDump(uint8_t dump[], size_t dumpsize, uint16_t keytable[]) { int res = 0; uint64_t t1 = msclock(); - for (i = 0 ; i * itemsize < dumpsize ; i++) { + for (uint16_t i = 0 ; i * itemsize < dumpsize ; i++) { + memcpy(attack, dump + i * itemsize, itemsize); + res = bruteforceItem(*attack, keytable); - if (res != PM3_SUCCESS) + if (res != PM3_SUCCESS) { break; + } } + free(attack); + t1 = msclock() - t1; - PrintAndLogEx(NORMAL, ""); + if (res == PM3_SUCCESS) { + PrintAndLogEx(NORMAL, ""); + } PrintAndLogEx(SUCCESS, "time " _YELLOW_("%" PRIu64) " seconds", t1 / 1000); if (res != PM3_SUCCESS) { - PrintAndLogEx(ERR, "loclass exiting. Try run " _YELLOW_("`hf iclass sim -t 2`") " again and collect new data"); + PrintAndLogEx(ERR, "loclass key recovery( %s )", _RED_("fail")); + PrintAndLogEx(HINT, "Try `" _YELLOW_("hf iclass sim -t 2") "` again and collect new data"); + PrintAndLogEx(NORMAL, ""); return PM3_ESOFT; } @@ -758,11 +788,12 @@ int bruteforceDump(uint8_t dump[], size_t dumpsize, uint16_t keytable[]) { // indicate crack-status. Those must be discarded for the // master key calculation uint8_t first16bytes[16] = {0}; - for (i = 0 ; i < 16 ; i++) { + for (uint8_t i = 0 ; i < 16 ; i++) { first16bytes[i] = keytable[i] & 0xFF; if ((keytable[i] & LOCLASS_CRACKED) != LOCLASS_CRACKED) { PrintAndLogEx(WARNING, "Warning: we are missing byte " _RED_("%d") " , custom key calculation will fail...", i); + PrintAndLogEx(NORMAL, ""); return PM3_ESOFT; } } @@ -873,7 +904,7 @@ static int _testHash1(void) { } int testElite(bool slowtests) { - PrintAndLogEx(INFO, "Testing iClass Elite functionality"); + PrintAndLogEx(INFO, "Testing iClass Elite functionality..."); PrintAndLogEx(INFO, "Testing hash2..."); uint8_t k_cus[8] = {0x5B, 0x7C, 0x62, 0xC4, 0x91, 0xC1, 0x1B, 0x39}; @@ -894,22 +925,23 @@ int testElite(bool slowtests) { */ uint8_t keytable[128] = {0}; hash2(k_cus, keytable); - printarr_human_readable("---------------------- Hash2 ----------------------", keytable, sizeof(keytable)); + printarr_human_readable("--------------------- " _CYAN_("Hash2") " -----------------------", keytable, sizeof(keytable)); if (keytable[3] == 0xA1 && keytable[0x30] == 0xA3 && keytable[0x6F] == 0x95) { - PrintAndLogEx(SUCCESS, " hash2 ( %s )", _GREEN_("ok")); + PrintAndLogEx(SUCCESS, " Hash2 ( %s )", _GREEN_("ok")); } int res = PM3_SUCCESS; PrintAndLogEx(INFO, "Testing hash1..."); res += _testHash1(); - PrintAndLogEx((res == PM3_SUCCESS) ? SUCCESS : WARNING, " hash1 ( %s )", (res == PM3_SUCCESS) ? _GREEN_("ok") : _RED_("fail")); + PrintAndLogEx((res == PM3_SUCCESS) ? SUCCESS : WARNING, " Hash1 ( %s )", (res == PM3_SUCCESS) ? _GREEN_("ok") : _RED_("fail")); PrintAndLogEx(INFO, "Testing key diversification..."); res += _test_iclass_key_permutation(); - PrintAndLogEx((res == PM3_SUCCESS) ? SUCCESS : WARNING, " key diversification ( %s )", (res == PM3_SUCCESS) ? _GREEN_("ok") : _RED_("fail")); + PrintAndLogEx((res == PM3_SUCCESS) ? SUCCESS : WARNING, " Key diversification ( %s )", (res == PM3_SUCCESS) ? _GREEN_("ok") : _RED_("fail")); - if (slowtests) + if (slowtests) { res += _testBruteforce(); + } return res; } diff --git a/client/src/loclass/ikeys.c b/client/src/loclass/ikeys.c index 9382074fb..b50699e9e 100644 --- a/client/src/loclass/ikeys.c +++ b/client/src/loclass/ikeys.c @@ -803,17 +803,17 @@ static bool des_getParityBitFromKey(uint8_t key) { } static void des_checkParity(uint8_t *key) { - int i; int fails = 0; - for (i = 0; i < 8; i++) { + for (uint8_t i = 0; i < 8; i++) { bool parity = des_getParityBitFromKey(key[i]); if (parity != (key[i] & 0x1)) { fails++; PrintAndLogEx(FAILED, "parity1 fail, byte %d [%02x] was %d, should be %d", i, key[i], (key[i] & 0x1), parity); } } + if (fails) { - PrintAndLogEx(FAILED, "parity fails: %d", fails); + PrintAndLogEx(FAILED, "parity fails... " _RED_("%d"), fails); } else { PrintAndLogEx(SUCCESS, " Key syntax is with parity bits inside each byte (%s)", _GREEN_("ok")); } @@ -894,15 +894,17 @@ static int testKeyDiversificationWithMasterkeyTestcases(uint8_t *key) { int i, error = 0; uint8_t empty[8] = {0}; - PrintAndLogEx(INFO, "Testing encryption/decryption"); + PrintAndLogEx(INFO, "Testing encryption/decryption..."); - for (i = 0; memcmp(testcases + i, empty, 8); i++) + for (i = 0; memcmp(testcases + i, empty, 8); i++) { error += testDES(key, testcases[i]); + } - if (error) - PrintAndLogEx(FAILED, "%d errors occurred (%d testcases)", error, i); - else - PrintAndLogEx(SUCCESS, "Hashing seems to work (%d testcases)", i); + if (error) { + PrintAndLogEx(FAILED, "%d errors occurred, %d testcases ( %s )", error, i, _RED_("fail")); + } else { + PrintAndLogEx(SUCCESS, " Hashing seems to work, " _YELLOW_("%d") " testcases ( %s )", i, _GREEN_("ok")); + } return error; } @@ -942,8 +944,9 @@ static int testDES2(uint8_t *key, uint64_t csn, uint64_t expected) { PrintAndLogEx(DEBUG, " {csn} %"PRIx64, crypt_csn); PrintAndLogEx(DEBUG, " expected %"PRIx64 " (%s)", expected, (expected == crypt_csn) ? _GREEN_("ok") : _RED_("fail")); - if (expected != crypt_csn) + if (expected != crypt_csn) { return PM3_ESOFT; + } return PM3_SUCCESS; } @@ -954,12 +957,12 @@ static int testDES2(uint8_t *key, uint64_t csn, uint64_t expected) { */ static int doTestsWithKnownInputs(void) { // KSel from http://www.proxmark.org/forum/viewtopic.php?pid=10977#p10977 - PrintAndLogEx(INFO, "Testing DES encryption"); + PrintAndLogEx(INFO, "Testing DES encryption... "); uint8_t key[8] = {0x6c, 0x8d, 0x44, 0xf9, 0x2a, 0x2d, 0x01, 0xbf}; testDES2(key, 0xbbbbaaaabbbbeeee, 0xd6ad3ca619659e6b); - PrintAndLogEx(INFO, "Testing hashing algorithm"); + PrintAndLogEx(INFO, "Testing hashing algorithm... "); int res = PM3_SUCCESS; res += testCryptedCSN(0x0102030405060708, 0x0bdd6512073c460a); @@ -973,10 +976,10 @@ static int doTestsWithKnownInputs(void) { res += testCryptedCSN(0x14e2adfc5bb7e134, 0x6ac90c6508bd9ea3); if (res != PM3_SUCCESS) { - PrintAndLogEx(FAILED, "%d res occurred (9 testcases)", res); + PrintAndLogEx(FAILED, "%d res occurred " _YELLOW_("9") " testcases ( %s )", res, _RED_("fail")); res = PM3_ESOFT; } else { - PrintAndLogEx(SUCCESS, "Hashing seems to work (9 testcases)"); + PrintAndLogEx(SUCCESS, " Hashing seems to work " _YELLOW_("9") " testcases ( %s )", _GREEN_("ok")); res = PM3_SUCCESS; } return res; @@ -986,6 +989,7 @@ int doKeyTests(void) { uint8_t key[8] = { 0xAE, 0xA6, 0x84, 0xA6, 0xDA, 0xB2, 0x32, 0x78 }; uint8_t parity[8] = {0x00, 0x01, 0x01, 0x01, 0x00, 0x01, 0x00, 0x01}; + for (int i = 0; i < 8; i++) { key[i] += parity[i]; } @@ -994,7 +998,6 @@ int doKeyTests(void) { des_checkParity(key); // Test hashing functions - PrintAndLogEx(SUCCESS, "The following tests require the correct 8-byte master key"); testKeyDiversificationWithMasterkeyTestcases(key); PrintAndLogEx(INFO, "Testing key diversification with non-sensitive keys..."); return doTestsWithKnownInputs(); diff --git a/client/src/mifare/mad.c b/client/src/mifare/mad.c index c72f33c1b..eae149a4c 100644 --- a/client/src/mifare/mad.c +++ b/client/src/mifare/mad.c @@ -449,8 +449,9 @@ int MADDFDecodeAndPrint(uint32_t short_aid, bool verbose) { } bool HasMADKey(uint8_t *d) { - if (d == NULL) + if (d == NULL) { return false; + } return (memcmp(d + (3 * MFBLOCK_SIZE), g_mifare_mad_key, sizeof(g_mifare_mad_key)) == 0); } diff --git a/client/src/mifare/mifarehost.c b/client/src/mifare/mifarehost.c index 6688a7842..f6fc0b07a 100644 --- a/client/src/mifare/mifarehost.c +++ b/client/src/mifare/mifarehost.c @@ -43,6 +43,8 @@ #include "cmdhf14a.h" #include "gen4.h" #include "parity.h" +#include "pmflash.h" +#include "preferences.h" // setDeviceDebugLevel int mf_dark_side(uint8_t blockno, uint8_t key_type, uint64_t *key) { uint32_t uid = 0; @@ -275,7 +277,7 @@ int mf_check_keys_fast_ex(uint8_t sectorsCnt, uint8_t firstChunk, uint8_t lastCh while (kbd_enter_pressed()) { SendCommandNG(CMD_BREAK_LOOP, NULL, 0); - PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "aborted via keyboard!"); return PM3_EOPABORTED; } @@ -305,12 +307,11 @@ int mf_check_keys_fast_ex(uint8_t sectorsCnt, uint8_t firstChunk, uint8_t lastCh uint8_t curr_keys = resp.oldarg[0]; if ((singleSectorParams >> 15) & 1) { - if (curr_keys) { - // uint64_t foo = bytes_to_num(resp.data.asBytes, 6); - PrintAndLogEx(NORMAL, ""); -// PrintAndLogEx(SUCCESS, "found Key %s for block %2i found: " _GREEN_("%012" PRIx64), (singleSectorParams >> 8) & 1 ? "B" : "A", singleSectorParams & 0xFF, foo); - PrintAndLogEx(SUCCESS, "\nTarget block %4u key type %c -- found valid key [ " _GREEN_("%s") " ]", + if (curr_keys) { + + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(SUCCESS, "\nTarget block " _GREEN_("%4u") " key type " _GREEN_("%c") " -- found valid key [ " _GREEN_("%s") " ]", singleSectorParams & 0xFF, ((singleSectorParams >> 8) & 1) ? 'B' : 'A', sprint_hex_inrow(resp.data.asBytes, MIFARE_KEY_SIZE) @@ -561,8 +562,9 @@ int mf_nested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo struct p *package = (struct p *)resp.data.asBytes; // error during nested on device side - if (package->isOK != PM3_SUCCESS) + if (package->isOK != PM3_SUCCESS) { return package->isOK; + } memcpy(&uid, package->cuid, sizeof(package->cuid)); @@ -582,12 +584,14 @@ int mf_nested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo pthread_t thread_id[2]; // create and run worker threads - for (uint8_t i = 0; i < 2; i++) + for (uint8_t i = 0; i < 2; i++) { pthread_create(thread_id + i, NULL, nested_worker_thread, &statelists[i]); + } // wait for threads to terminate: - for (uint8_t i = 0; i < 2; i++) + for (uint8_t i = 0; i < 2; i++) { pthread_join(thread_id[i], (void *)&statelists[i].head.slhead); + } // the first 16 Bits of the cryptostate already contain part of our key. // Create the intersection of the two lists based on these 16 Bits and @@ -596,9 +600,11 @@ int mf_nested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo p2 = p4 = statelists[1].head.slhead; while (p1 <= statelists[0].tail.sltail && p2 <= statelists[1].tail.sltail) { + if (Compare16Bits(p1, p2) == 0) { struct Crypto1State savestate; + savestate = *p1; while (Compare16Bits(p1, &savestate) == 0 && p1 <= statelists[0].tail.sltail) { *p3 = *p1; @@ -606,6 +612,7 @@ int mf_nested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo p3++; p1++; } + savestate = *p2; while (Compare16Bits(p2, &savestate) == 0 && p2 <= statelists[1].tail.sltail) { *p4 = *p2; @@ -613,6 +620,7 @@ int mf_nested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo p4++; p2++; } + } else { while (Compare16Bits(p1, p2) == -1) p1++; while (Compare16Bits(p1, p2) == 1) p2++; @@ -635,13 +643,15 @@ int mf_nested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo // Create the intersection statelists[0].len = intersection(statelists[0].head.keyhead, statelists[1].head.keyhead); + bool looped = false; + //statelists[0].tail.keytail = --p7; uint32_t keycnt = statelists[0].len; if (keycnt == 0) { goto out; } - PrintAndLogEx(SUCCESS, "Found " _YELLOW_("%u") " key candidates", keycnt); + PrintAndLogEx(SUCCESS, "Found " _YELLOW_("%u") " key candidate%c", keycnt, (keycnt > 1) ? 's' : ' '); memset(resultKey, 0, MIFARE_KEY_SIZE); uint64_t key64 = -1; @@ -659,44 +669,53 @@ int mf_nested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo register uint8_t j; for (j = 0; j < size; j++) { crypto1_get_lfsr(statelists[0].head.slhead + i, &key64); - num_to_bytes(key64, 6, keyBlock + j * MIFARE_KEY_SIZE); + num_to_bytes(key64, MIFARE_KEY_SIZE, keyBlock + j * MIFARE_KEY_SIZE); } if (mf_check_keys(statelists[0].blockNo, statelists[0].keyType, false, size, keyBlock, &key64) == PM3_SUCCESS) { + + if (looped) { + PrintAndLogEx(NORMAL, ""); + } + free(statelists[0].head.slhead); free(statelists[1].head.slhead); - num_to_bytes(key64, 6, resultKey); + num_to_bytes(key64, MIFARE_KEY_SIZE, resultKey); if (package->keytype < 2) { - PrintAndLogEx(SUCCESS, "\nTarget block %4u key type %c -- found valid key [ " _GREEN_("%s") " ]", + PrintAndLogEx(SUCCESS, "Target block " _GREEN_("%4u") " key type " _GREEN_("%c") " -- found valid key [ " _GREEN_("%s") " ]", package->block, package->keytype ? 'B' : 'A', sprint_hex_inrow(resultKey, MIFARE_KEY_SIZE) ); } else { - PrintAndLogEx(SUCCESS, "\nTarget block %4u key type %02x -- found valid key [ " _GREEN_("%s") " ]", + PrintAndLogEx(SUCCESS, "Target block " _GREEN_("%4u") " key type " _GREEN_("%02x") " -- found valid key [ " _GREEN_("%s") " ]", package->block, MIFARE_AUTH_KEYA + package->keytype, sprint_hex_inrow(resultKey, MIFARE_KEY_SIZE) ); } - - return PM3_SUCCESS; } float bruteforce_per_second = (float)(i + max_keys) / ((msclock() - start_time) / 1000.0); PrintAndLogEx(INPLACE, "%6d/%u keys | %5.1f keys/sec | worst case %6.1f seconds remaining", i, keycnt, bruteforce_per_second, (keycnt - i) / bruteforce_per_second); + looped = true; } out: + + if (looped) { + PrintAndLogEx(NORMAL, ""); + } + if (package->keytype < 2) { - PrintAndLogEx(SUCCESS, "\nTarget block %4u key type %c", + PrintAndLogEx(SUCCESS, "Target block " _YELLOW_("%4u") " key type " _YELLOW_("%c"), package->block, package->keytype ? 'B' : 'A' ); } else { - PrintAndLogEx(SUCCESS, "\nTarget block %4u key type %02x", + PrintAndLogEx(SUCCESS, "Target block " _YELLOW_("%4u") " key type " _YELLOW_("%02x"), package->block, MIFARE_AUTH_KEYA + package->keytype ); @@ -911,10 +930,11 @@ int mf_static_nested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trg num_to_bytes(key64, MIFARE_KEY_SIZE, resultKey); - if (IfPm3Flash() && keycnt > 70) + if (IfPm3Flash() && keycnt > 70) { PrintAndLogEx(NORMAL, ""); + } - PrintAndLogEx(SUCCESS, "target block %4u key type %c -- found valid key [ " _GREEN_("%s") " ]", + PrintAndLogEx(SUCCESS, "target block " _GREEN_("%4u") " key type " _GREEN_("%c") " -- found valid key [ " _GREEN_("%s") " ]", package->block, package->keytype ? 'B' : 'A', sprint_hex_inrow(resultKey, MIFARE_KEY_SIZE) @@ -1535,10 +1555,21 @@ int detect_classic_static_encrypted_nonce_ex(uint8_t block_no, uint8_t key_type, cdata[21] = corruptnrar; cdata[22] = corruptnrarparity; + uint8_t dbg_curr = DBG_NONE; + if (getDeviceDebugLevel(&dbg_curr) != PM3_SUCCESS) { + return PM3_EFAILED; + } + + if (setDeviceDebugLevel(verbose ? MAX(dbg_curr, DBG_INFO) : DBG_NONE, false) != PM3_SUCCESS) { + return PM3_EFAILED; + } + clearCommandBuffer(); SendCommandNG(CMD_HF_MIFARE_STATIC_ENCRYPTED_NONCE, cdata, sizeof(cdata)); PacketResponseNG resp; - if (WaitForResponseTimeout(CMD_HF_MIFARE_STATIC_ENCRYPTED_NONCE, &resp, 1000)) { + if (WaitForResponseTimeout(CMD_HF_MIFARE_STATIC_ENCRYPTED_NONCE, &resp, 1500)) { + + setDeviceDebugLevel(dbg_curr, false); if (resp.status == PM3_ESOFT) { return NONCE_FAIL; @@ -1604,6 +1635,8 @@ int detect_classic_static_encrypted_nonce_ex(uint8_t block_no, uint8_t key_type, } return resp.data.asBytes[0]; } + + setDeviceDebugLevel(dbg_curr, false); return NONCE_FAIL; } diff --git a/client/src/proxmark3.c b/client/src/proxmark3.c index 67bda5628..b916f0b71 100644 --- a/client/src/proxmark3.c +++ b/client/src/proxmark3.c @@ -308,16 +308,18 @@ static bool DetectWindowsAnsiSupport(void) { #endif // disable colors if stdin or stdout are redirected - if ((! g_session.stdinOnTTY) || (! g_session.stdoutOnTTY)) + if ((! g_session.stdinOnTTY) || (! g_session.stdoutOnTTY)) { return false; + } HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); DWORD dwMode = 0; GetConsoleMode(hOut, &dwMode); //ENABLE_VIRTUAL_TERMINAL_PROCESSING is already set - if ((dwMode & ENABLE_VIRTUAL_TERMINAL_PROCESSING)) + if ((dwMode & ENABLE_VIRTUAL_TERMINAL_PROCESSING)) { return true; + } dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; @@ -337,11 +339,13 @@ int push_cmdscriptfile(char *path, bool stayafter) { } FILE *f = fopen(path, "r"); - if (f == NULL) + if (f == NULL) { return PM3_EFILE; + } - if (cmdscriptfile_idx == 0) + if (cmdscriptfile_idx == 0) { cmdscriptfile_stayafter = stayafter; + } cmdscriptfile[++cmdscriptfile_idx] = f; return PM3_SUCCESS; @@ -373,28 +377,32 @@ main_loop(const char *script_cmds_file, char *script_cmd, bool stayInCommandLoop bool execCommand = (script_cmd != NULL); bool fromInteractive = false; uint16_t script_cmd_len = 0; + if (execCommand) { script_cmd_len = strlen(script_cmd); str_creplace(script_cmd, script_cmd_len, ';', '\0'); } + bool stdinOnPipe = !isatty(STDIN_FILENO); char script_cmd_buf[256] = {0x00}; // iceman, needs lua script the same file_path_buffer as the rest // cache Version information now: - if (execCommand || script_cmds_file || stdinOnPipe) + if (execCommand || script_cmds_file || stdinOnPipe) { pm3_version(false, false); - else + } else { pm3_version_short(); + } if (script_cmds_file) { char *path; int res = searchFile(&path, CMD_SCRIPTS_SUBDIR, script_cmds_file, ".cmd", false); if (res == PM3_SUCCESS) { - if (push_cmdscriptfile(path, stayInCommandLoop) == PM3_SUCCESS) + if (push_cmdscriptfile(path, stayInCommandLoop) == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, "executing commands from file: %s\n", path); - else + } else { PrintAndLogEx(ERR, "could not open " _YELLOW_("%s") "...", path); + } free(path); } } @@ -451,20 +459,23 @@ check_script: prompt_ctx = stdinOnPipe ? PROXPROMPT_CTX_STDIN : PROXPROMPT_CTX_SCRIPTCMD; cmd = str_dup(script_cmd); - if ((cmd != NULL) && (! fromInteractive)) + if ((cmd != NULL) && (! fromInteractive)) { printprompt = true; + } uint16_t len = strlen(script_cmd) + 1; script_cmd += len; - if (script_cmd_len == len - 1) + if (script_cmd_len == len - 1) { execCommand = false; + } script_cmd_len -= len; } else { // exit after exec command - if (script_cmd && !stayInCommandLoop) + if (script_cmd && !stayInCommandLoop) { break; + } // if there is a pipe from stdin if (stdinOnPipe) { @@ -554,22 +565,27 @@ check_script: mainret = CommandReceived(cmd); // exit or quit - if (mainret == PM3_EFATAL) + if (mainret == PM3_EFATAL) { break; + } + if (mainret == PM3_SQUIT) { // Normal quit, map to 0 mainret = PM3_SUCCESS; break; } } + free(cmd); cmd = NULL; + } else { PrintAndLogEx(NORMAL, "\n"); - if (script_cmds_file && stayInCommandLoop) + if (script_cmds_file && stayInCommandLoop) { stayInCommandLoop = false; - else + } else { break; + } } } // end while @@ -618,8 +634,9 @@ const char *get_my_executable_directory(void) { static void set_my_executable_path(void) { int path_length = wai_getExecutablePath(NULL, 0, NULL); - if (path_length == -1) + if (path_length == -1) { return; + } my_executable_path = (char *)calloc(path_length + 1, sizeof(uint8_t)); int dirname_length = 0; @@ -844,12 +861,13 @@ finish2: CloseProxmark(g_session.current_device); finish: - if (ret == PM3_SUCCESS) + if (ret == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, _CYAN_("All done")); - else if (ret == PM3_EOPABORTED) + } else if (ret == PM3_EOPABORTED) { PrintAndLogEx(FAILED, "Aborted by user"); - else + } else { PrintAndLogEx(ERR, "Aborted on error %u", ret); + } return ret; } @@ -908,8 +926,9 @@ static int flash_pm3(char *serial_port_name, uint8_t num_files, const char *file goto finish; } - if (num_files == 0) + if (num_files == 0) { goto finish; + } for (int i = 0 ; i < num_files; ++i) { ret = flash_prepare(&files[i], can_write_bl, max_allowed * ONE_KB); @@ -938,12 +957,15 @@ finish2: for (int i = 0 ; i < num_files; ++i) { flash_free(&files[i]); } - if (ret == PM3_SUCCESS) + + if (ret == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, _CYAN_("All done")); - else if (ret == PM3_EOPABORTED) + } else if (ret == PM3_EOPABORTED) { PrintAndLogEx(FAILED, "Aborted by user"); - else + } else { PrintAndLogEx(ERR, "Aborted on error"); + } + PrintAndLogEx(INFO, "\nHave a nice day!"); return ret; } @@ -1054,6 +1076,7 @@ int main(int argc, char *argv[]) { show_help(false, exec_name); return 1; } + if (port != NULL) { // We got already one PrintAndLogEx(ERR, _RED_("ERROR:") " cannot parse command line. We got " _YELLOW_("%s") " as port and now we got also: " _YELLOW_("%s") "\n", port, argv[i + 1]); @@ -1315,21 +1338,22 @@ int main(int argc, char *argv[]) { // This will allow the command line to override the settings.json values preferences_load(); // quick patch for debug level - if (! debug_mode_forced) { + if (debug_mode_forced == false) { g_debugMode = g_session.client_debug_level; } // settings_save (); // End Settings // even if prefs, we disable colors if stdin or stdout is not a TTY - if ((! g_session.stdinOnTTY) || (! g_session.stdoutOnTTY)) { + if ((g_session.stdinOnTTY == false) || (g_session.stdoutOnTTY == false)) { g_session.supports_colors = false; g_session.emoji_mode = EMO_ALTTEXT; } // Let's take a baudrate ok for real UART, USB-CDC & BT don't use that info anyway - if (speed == 0) + if (speed == 0) { speed = USART_BAUD_RATE; + } if (dumpmem_mode) { dumpmem_pm3(port, dumpmem_filename, dumpmem_addr, dumpmem_len, dumpmem_raw); @@ -1347,8 +1371,9 @@ int main(int argc, char *argv[]) { } if (script_cmd) { - while (script_cmd[strlen(script_cmd) - 1] == ' ') + while (script_cmd[strlen(script_cmd) - 1] == ' ') { script_cmd[strlen(script_cmd) - 1] = 0x00; + } if (strlen(script_cmd) == 0) { script_cmd = NULL; @@ -1381,23 +1406,23 @@ int main(int argc, char *argv[]) { CloseProxmark(g_session.current_device); } - if ((port != NULL) && (!g_session.pm3_present)) { + if ((port != NULL) && (g_session.pm3_present == false)) { exit(EXIT_FAILURE); } - if (!g_session.pm3_present) { + if (g_session.pm3_present == false) { PrintAndLogEx(INFO, _YELLOW_("OFFLINE") " mode. Check " _YELLOW_("\"%s -h\"") " if it's not what you want.\n", exec_name); } // ascii art only in interactive client - if (!script_cmds_file && !script_cmd && g_session.stdinOnTTY && g_session.stdoutOnTTY && !dumpmem_mode && !flash_mode && !reboot_bootloader_mode) { + if (!script_cmds_file && !script_cmd && g_session.stdinOnTTY && g_session.stdoutOnTTY && (dumpmem_mode == false) && (flash_mode == false) && (reboot_bootloader_mode == false)) { showBanner(); } // Save settings if not loaded from settings json file. // Doing this here will ensure other checks and updates are saved to over rule default // e.g. Linux color use check - if ((!g_session.preferences_loaded) && (!g_session.incognito)) { + if ((g_session.preferences_loaded == false) && (g_session.incognito == false)) { PrintAndLogEx(INFO, "Creating initial preferences file"); // json save reports file name, so just info msg here preferences_save(); // Save defaults g_session.preferences_loaded = true; @@ -1417,7 +1442,7 @@ int main(int argc, char *argv[]) { #ifdef HAVE_GUI -# if defined(_WIN32) +# if defined(_WIN32) || (defined(__MACH__) && defined(__APPLE__)) InitGraphics(argc, argv, script_cmds_file, script_cmd, stayInCommandLoop); MainGraphics(); # else diff --git a/client/src/util.c b/client/src/util.c index f5c3735d9..541b67dd8 100644 --- a/client/src/util.c +++ b/client/src/util.c @@ -1606,3 +1606,37 @@ uint8_t get_highest_frequency(const uint8_t *d, uint8_t n) { PrintAndLogEx(DEBUG, "highest occurance... %u xor byte... 0x%02X", highest, v); return v; } + +size_t unduplicate(uint8_t *d, size_t n, const uint8_t item_n) { + if (n == 0) { + return 0; + } + + int write_index = 0; + + for (int read_index = 0; read_index < n; ++read_index) { + uint8_t *current = d + read_index * item_n; + + bool is_duplicate = false; + + // Check against all previous unique elements + for (int i = 0; i < write_index; ++i) { + uint8_t *unique = d + i * item_n; + if (memcmp(current, unique, item_n) == 0) { + is_duplicate = 1; + break; + } + } + + // If not duplicate, move to the write_index position + if (is_duplicate == false) { + uint8_t *dest = d + write_index * item_n; + if (dest != current) { + memcpy(dest, current, item_n); + } + write_index++; + } + } + + return write_index; +} diff --git a/client/src/util.h b/client/src/util.h index 0ca53591e..c80e602f4 100644 --- a/client/src/util.h +++ b/client/src/util.h @@ -194,4 +194,7 @@ struct smartbuf { void sb_append_char(smartbuf *sb, unsigned char c); uint8_t get_highest_frequency(const uint8_t *d, uint8_t n); + +size_t unduplicate(uint8_t *d, size_t n, const uint8_t item_n); + #endif diff --git a/common/crapto1/crypto1.c b/common/crapto1/crypto1.c index 78d42cec4..8ffe04fdb 100644 --- a/common/crapto1/crypto1.c +++ b/common/crapto1/crypto1.c @@ -35,8 +35,9 @@ int filter(uint32_t const x) { (x = (x >> 8 & 0xff00ff) | (x & 0xff00ff) << 8, x = x >> 16 | x << 16) void crypto1_init(struct Crypto1State *state, uint64_t key) { - if (state == NULL) + if (state == NULL) { return; + } state->odd = 0; state->even = 0; for (int i = 47; i > 0; i -= 2) { @@ -53,7 +54,9 @@ void crypto1_deinit(struct Crypto1State *state) { #if !defined(__arm__) || defined(__linux__) || defined(_WIN32) || defined(__APPLE__) // bare metal ARM Proxmark lacks calloc()/free() struct Crypto1State *crypto1_create(uint64_t key) { struct Crypto1State *state = calloc(sizeof(*state), sizeof(uint8_t)); - if (!state) return NULL; + if (state == NULL) { + return NULL; + } crypto1_init(state, key); return state; } @@ -145,8 +148,8 @@ uint32_t crypto1_word(struct Crypto1State *s, uint32_t in, int is_encrypted) { */ uint32_t prng_successor(uint32_t x, uint32_t n) { SWAPENDIAN(x); - while (n--) + while (n--) { x = x >> 1 | (x >> 16 ^ x >> 18 ^ x >> 19 ^ x >> 21) << 31; - + } return SWAPENDIAN(x); } diff --git a/common_arm/ticks.c b/common_arm/ticks.c index 4abda2689..73182a11c 100644 --- a/common_arm/ticks.c +++ b/common_arm/ticks.c @@ -116,8 +116,9 @@ uint32_t RAMFUNC GetTickCount(void) { uint32_t RAMFUNC GetTickCountDelta(uint32_t start_ticks) { uint32_t stop_ticks = AT91C_BASE_RTTC->RTTC_RTVR; - if (stop_ticks >= start_ticks) + if (stop_ticks >= start_ticks) { return stop_ticks - start_ticks; + } return (UINT32_MAX - start_ticks) + stop_ticks; } diff --git a/doc/commands.json b/doc/commands.json index 362333ef8..034e055b1 100644 --- a/doc/commands.json +++ b/doc/commands.json @@ -1382,7 +1382,8 @@ "hf 14a sim -t 9 -> FM11RF005SH Shanghai Metro", "hf 14a sim -t 10 -> ST25TA IKEA Rothult", "hf 14a sim -t 11 -> Javacard (JCOP)", - "hf 14a sim -t 12 -> 4K Seos card" + "hf 14a sim -t 12 -> 4K Seos card", + "hf 14a sim -t 13 -> MIFARE Ultralight C" ], "offline": false, "options": [ @@ -1392,9 +1393,11 @@ "-n, --num Exit simulation after blocks have been read by reader. 0 = infinite", "-x Performs the 'reader attack', nr/ar attack against a reader", "--sk Fill simulator keys from found keys", - "-v, --verbose verbose output" + "-v, --verbose verbose output", + "--c1 UL-C Auth - all zero handshake part 1", + "--c2 UL-C Auth - all zero handshake part 2" ], - "usage": "hf 14a sim [-hxv] -t <1-12> [-u ] [-n ] [--sk]" + "usage": "hf 14a sim [-hxv] -t <1-12> [-u ] [-n ] [--sk] [--c1] [--c2]" }, "hf 14a simaid": { "command": "hf 14a simaid", @@ -7398,21 +7401,24 @@ }, "hf mfu sim": { "command": "hf mfu sim", - "description": "Simulate MIFARE Ultralight family type based upon ISO/IEC 14443 type A tag with 4,7 or 10 byte UID from emulator memory. See `hf mfu eload` first. The UID from emulator memory will be used if not specified. See `hf 14a sim -h` to see available types. You want 2 or 7 usually.", + "description": "Simulate MIFARE Ultralight family type based upon ISO/IEC 14443 type A tag with 4,7 or 10 byte UID from emulator memory. See `hf mfu eload` first. The UID from emulator memory will be used if not specified. See `hf 14a sim -h` to see available types. You want 2, 7 or 13 usually.", "notes": [ "hf mfu sim -t 2 --uid 11223344556677 -> MIFARE Ultralight", "hf mfu sim -t 7 --uid 11223344556677 -n 5 -> MFU EV1 / NTAG 215 Amiibo", - "hf mfu sim -t 7 -> MFU EV1 / NTAG 215 Amiibo" + "hf mfu sim -t 7 -> MFU EV1 / NTAG 215 Amiibo", + "hf mfu sim -t 13 -> MIFARE Ultralight-C" ], "offline": false, "options": [ "-h, --help This help", - "-t, --type <1..12> Simulation type to use", + "-t, --type <1..13> Simulation type to use", "-u, --uid <4|7|10> hex bytes UID", "-n, --num Exit simulation after blocks. 0 = infinite", - "-v, --verbose Verbose output" + "-v, --verbose Verbose output", + "--c1 UL-C Auth - all zero handshake part 1", + "--c2 UL-C Auth - all zero handshake part 2" ], - "usage": "hf mfu sim [-hv] -t <1..12> [-u ] [-n ]" + "usage": "hf mfu sim [-hv] -t <1..13> [-u ] [-n ] [--c1] [--c2]" }, "hf mfu tamper": { "command": "hf mfu tamper", @@ -12121,9 +12127,10 @@ "notes": [ "mem load -f myfile -> upload file myfile values at default offset 0", "mem load -f myfile -o 1024 -> upload file myfile values at offset 1024", - "mem load -f mfc_default_keys -m -> upload MFC keys", + "mem load -f mfc_default_keys -m -> upload MIFARE Classic keys", "mem load -f t55xx_default_pwds -t -> upload T55XX passwords", - "mem load -f iclass_default_keys -i -> upload iCLASS keys" + "mem load -f iclass_default_keys -i -> upload iCLASS keys", + "mem load -f mfulc_default_keys --ulc -> upload MIFARE UL-C keys" ], "offline": false, "options": [ @@ -12132,9 +12139,11 @@ "-m, --mifare, --mfc upload 6 bytes keys (mifare key dictionary)", "-i, --iclass upload 8 bytes keys (iClass key dictionary)", "-t, --t55xx upload 4 bytes keys (password dictionary)", + "--ulc upload 16 bytes keys (mifare UL-C key dictionary)", + "--ulaes upload 16 bytes keys (mifare UL-AES key dictionary)", "-f, --file file name" ], - "usage": "mem load [-hmit] [-o ] -f " + "usage": "mem load [-hmit] [-o ] [--ulc] [--ulaes] -f " }, "mem spiffs check": { "command": "mem spiffs check", @@ -13365,6 +13374,6 @@ "metadata": { "commands_extracted": 768, "extracted_by": "PM3Help2JSON v1.00", - "extracted_on": "2025-06-17T16:11:53" + "extracted_on": "2025-06-19T15:01:51" } } diff --git a/include/ansi.h b/include/ansi.h index 20815bc03..b791741f5 100644 --- a/include/ansi.h +++ b/include/ansi.h @@ -21,6 +21,12 @@ #define AEND "\x1b[0m" +#define _CLEAR_ "\x1b[2J" +#define _CLEAR_SCROLLBACK_ "\x1b[3J" +#define _TOP_ "\x1b[1;1f" + +#define _CLR_ "\x1b[0K" + #define _BLACK_(s) "\x1b[30m" s AEND #define _RED_(s) "\x1b[31m" s AEND #define _GREEN_(s) "\x1b[32m" s AEND @@ -57,10 +63,6 @@ #define _BACK_BRIGHT_CYAN_(s) "\x1b[46;1m" s AEND #define _BACK_BRIGHT_WHITE_(s) "\x1b[47;1m" s AEND -#define _CLEAR_ "\x1b[2J" -#define _CLEAR_SCROLLBACK_ "\x1b[3J" -#define _TOP_ "\x1b[1;1f" - #if defined(HAVE_READLINE) // https://wiki.hackzine.org/development/misc/readline-color-prompt.html // Applications may indicate that the prompt contains diff --git a/include/pm3_cmd.h b/include/pm3_cmd.h index 82623f2dd..bb9123d7d 100644 --- a/include/pm3_cmd.h +++ b/include/pm3_cmd.h @@ -349,6 +349,12 @@ typedef struct { uint8_t key[6]; } PACKED mfc_eload_t; +typedef struct { + bool use_flashmem; + uint16_t keycount; + uint8_t keys[]; +} PACKED mfulc_keys_t; + typedef struct { uint8_t status; uint8_t CSN[8]; diff --git a/include/pmflash.h b/include/pmflash.h index 7820ad4e2..7e601c2bc 100644 --- a/include/pmflash.h +++ b/include/pmflash.h @@ -79,6 +79,13 @@ #define MF_KEYS_FILE "dict_mf.bin" #define MF_KEY_LENGTH 6 +// MIFARE Ultralight-C keys stored in spiffs +#define MFULC_KEYS_FILE "dict_mfulc.bin" +#define MFULC_KEY_LENGTH (16) + +// MIFARE Ultralight-AES keys stored in spiffs +#define MFULAES_KEYS_FILE "dict_mfulaes.bin" +#define MFULAES_KEY_LENGTH (16) // RDV40, validation structure to help identifying that client/firmware is talking with RDV40 typedef struct { uint8_t magic[4]; diff --git a/tools/pm3_tests.sh b/tools/pm3_tests.sh index 65a2f9985..f8e2dba2b 100755 --- a/tools/pm3_tests.sh +++ b/tools/pm3_tests.sh @@ -573,8 +573,8 @@ while true; do if ! CheckExecute slow "hf iclass loclass long test" "$CLIENTBIN -c 'hf iclass loclass --long'" "verified \( ok \)"; then break; fi if ! CheckExecute slow "emv long test" "$CLIENTBIN -c 'emv test -l'" "Tests \( ok"; then break; fi if ! CheckExecute "hf iclass lookup test" "$CLIENTBIN -c 'hf iclass lookup --csn 9655a400f8ff12e0 --epurse f0ffffffffffffff --macs 0000000089cb984b -f $DICPATH/iclass_default_keys.dic'" \ - "valid key AE A6 84 A6 DA B2 32 78"; then break; fi - if ! CheckExecute "hf iclass loclass test" "$CLIENTBIN -c 'hf iclass loclass --test'" "key diversification \( ok \)"; then break; fi + "valid key AEA684A6DAB23278"; then break; fi + if ! CheckExecute "hf iclass loclass test" "$CLIENTBIN -c 'hf iclass loclass --test'" "Key diversification \( ok \)"; then break; fi if ! CheckExecute "emv test" "$CLIENTBIN -c 'emv test'" "Tests \( ok"; then break; fi if ! CheckExecute "hf cipurse test" "$CLIENTBIN -c 'hf cipurse test'" "Tests \( ok"; then break; fi if ! CheckExecute "hf mfdes test" "$CLIENTBIN -c 'hf mfdes test'" "Tests \( ok"; then break; fi