mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2025-07-05 12:36:04 -07:00
added Ultralight-C simulation. hf mfu sim -t 13. Use eload first. Also added support to upload UL-C dictionaries and UL-AES to spiffs memory. A lot of textual reworked across client. Unifiy texts and a bit more color ;)
This commit is contained in:
parent
0e87f01ab9
commit
65607fc727
52 changed files with 1074 additions and 430 deletions
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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!"));
|
||||
|
|
|
@ -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!"));
|
||||
|
|
|
@ -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!");
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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: {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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", "<dec>", "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", "<fn>", "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"},
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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", "<hex>", "<4|7|10> hex bytes UID"),
|
||||
arg_int0("n", "num", "<dec>", "Exit simulation after <numreads> 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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -20,7 +20,8 @@
|
|||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <pthread.h> // spinlock
|
||||
#include <stdlib.h> // 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));
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 <dec> Exit simulation after <numreads> 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 <hex>] [-n <dec>] [--sk]"
|
||||
"usage": "hf 14a sim [-hxv] -t <1-12> [-u <hex>] [-n <dec>] [--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 <hex> <4|7|10> hex bytes UID",
|
||||
"-n, --num <dec> Exit simulation after <numreads> 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 <hex>] [-n <dec>]"
|
||||
"usage": "hf mfu sim [-hv] -t <1..13> [-u <hex>] [-n <dec>] [--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 <fn> file name"
|
||||
],
|
||||
"usage": "mem load [-hmit] [-o <dec>] -f <fn>"
|
||||
"usage": "mem load [-hmit] [-o <dec>] [--ulc] [--ulaes] -f <fn>"
|
||||
},
|
||||
"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"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue