Merge branch 'RfidResearchGroup:master' into master

This commit is contained in:
ry4000
2025-06-02 17:50:07 +10:00
committed by GitHub
26 changed files with 1425 additions and 275 deletions
+6 -1
View File
@@ -3,17 +3,22 @@ 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 `hf iclass tear` - readability improvements for erase phase (@antiklesys)
- Changed `hf iclass legrec` - code optimizations gaining a ~8% speed increase (@antiklesys)
- Modified `hf iclass tear` - now has a device side implementation also. (@antiklesys) (@iceman1001)
- Changed `hf iclass info` - now uses CSN values based checks (@antiklesys)
- Changed `hf iclass dump` - now uses default AA1 key when called without a key or key index (@iceman1001)
- Renamed `hf iclass trbl` to `hf iclass tear` (@iceman1001)
- Changed `hw tearoff` - the device side message is now debug log controlled (@iceman1001)
- Changed `pm3.sh` - Serial ports enumeration on Proxspace3.xx / MINGW environments, now using powershell.exe since wmic is deprecated (@iceman1001)
- Fixed and updated `hf iclass trbl` to correctly use the credit key when passed and show partial tearoff results (@antiklesys)
- Fixed `hf iclass legbrute` was not correctly parsin the index value
- Fixed `hf iclass legbrute` was not correctly parsing the index value
- Fixed `hf mf ekeyprn` - failed to download emulator memory due to wrong size calculation (@iceman1001)
- Fixed `hf mf fchk --mem` to actually use flash dict (@doegox)
- Fixed `make install` on OSX thanks DaveItsLong (@doegox)
- Added new standalone mode `HF_ST25_TEAROFF` to store/restore ST25TB tags with tearoff for counters (@seclabz)
- Added `hf_mfu_ultra.lua` script enables restoring dump to ULTRA/UL-5 tags and clearing previously written ULTRA tags (@mak-42)
- Fixed `hf mfu sim` to make persistent the counter increases in the emulator memory (@sup3rgiu)
## [Blue Ice.4.20142][2025-03-25]
- Added `des_talk.py` script for easier MIFARE DESFire handling (@trigat)
+1 -3
View File
@@ -233,10 +233,8 @@ static void become_card(void) {
tag_response_info_t *canned;
uint32_t cuid;
uint32_t counters[3] = { 0 };
uint8_t tearings[3] = { 0xbd, 0xbd, 0xbd };
uint8_t pages;
SimulateIso14443aInit(tagType, flags, data, NULL, 0, &canned, &cuid, counters, tearings, &pages);
SimulateIso14443aInit(tagType, flags, data, NULL, 0, &canned, &cuid, &pages);
DbpString(_CYAN_("[@]") " Setup done - entering emulation loop");
int fromReaderLen;
+1 -1
View File
@@ -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, NULL, NULL) == false) {
if (SimulateIso14443aInit(11, flags, data, NULL, 0, &responses, &cuid, NULL) == false) {
BigBuf_free_keep_EM();
reply_ng(CMD_HF_MIFARE_SIMULATE, PM3_EINIT, NULL, 0);
DbpString(_RED_("Error initializing the emulation process!"));
+1 -1
View File
@@ -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, NULL, NULL) == false) {
if (SimulateIso14443aInit(4, flags, data, NULL, 0, &responses, &cuid, NULL) == false) {
BigBuf_free_keep_EM();
reply_ng(CMD_HF_MIFARE_SIMULATE, PM3_EINIT, NULL, 0);
DbpString(_RED_("Error initializing the emulation process!"));
+2 -4
View File
@@ -118,8 +118,6 @@ void RunMod(void) {
uint8_t tagType = 10; // 10 = ST25TA IKEA Rothult
tag_response_info_t *responses;
uint32_t cuid = 0;
uint32_t counters[3] = { 0x00, 0x00, 0x00 };
uint8_t tearings[3] = { 0xbd, 0xbd, 0xbd };
uint8_t pages = 0;
// command buffers
@@ -193,7 +191,7 @@ void RunMod(void) {
memcpy(data, stuid, sizeof(stuid));
if (SimulateIso14443aInit(tagType, flags, data, NULL, 0, &responses, &cuid, counters, tearings, &pages) == false) {
if (SimulateIso14443aInit(tagType, flags, data, NULL, 0, &responses, &cuid, &pages) == false) {
BigBuf_free_keep_EM();
reply_ng(CMD_HF_MIFARE_SIMULATE, PM3_EINIT, NULL, 0);
DbpString(_YELLOW_("!!") "Error initializing the simulation process!");
@@ -371,7 +369,7 @@ void RunMod(void) {
memcpy(data, stuid, sizeof(stuid));
if (SimulateIso14443aInit(tagType, flags, data, NULL, 0, &responses, &cuid, counters, tearings, &pages) == false) {
if (SimulateIso14443aInit(tagType, flags, data, NULL, 0, &responses, &cuid, &pages) == false) {
BigBuf_free_keep_EM();
reply_ng(CMD_HF_MIFARE_SIMULATE, PM3_EINIT, NULL, 0);
DbpString(_YELLOW_("!!") "Error initializing the simulation process!");
+5
View File
@@ -99,6 +99,7 @@ int tearoff_hook(void) {
if (g_tearoff_enabled) {
if (g_tearoff_delay_us == 0) {
Dbprintf(_RED_("No tear-off delay configured!"));
g_tearoff_enabled = false;
return PM3_SUCCESS; // SUCCESS = the hook didn't do anything
}
SpinDelayUsPrecision(g_tearoff_delay_us);
@@ -2229,6 +2230,10 @@ static void PacketReceived(PacketCommandNG *packet) {
iclass_credit_epurse((iclass_credit_epurse_t *)packet->data.asBytes);
break;
}
case CMD_HF_ICLASS_TEARBL: {
iClass_TearBlock((iclass_tearblock_req_t *)packet->data.asBytes);
break;
}
#endif
#ifdef WITH_HFSNIFF
+535 -60
View File
File diff suppressed because it is too large Load Diff
+1
View File
@@ -72,4 +72,5 @@ uint8_t get_pagemap(const picopass_hdr_t *hdr);
void iclass_send_as_reader(uint8_t *frame, int len, uint32_t *start_time, uint32_t *end_time, bool shallow_mod);
void iClass_Recover(iclass_recover_req_t *msg);
void iClass_TearBlock(iclass_tearblock_req_t *msg);
#endif
+28 -24
View File
@@ -1184,7 +1184,7 @@ bool prepare_allocated_tag_modulation(tag_response_info_t *response_info, uint8_
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, uint32_t counters[3], uint8_t tearings[3], uint8_t *pages) {
uint32_t *cuid, uint8_t *pages) {
uint8_t sak = 0;
// The first response contains the ATQA (note: bytes are transmitted in reverse order).
static uint8_t rATQA[2] = { 0x00 };
@@ -1231,14 +1231,11 @@ bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data,
mfu_dump_t *mfu_header = (mfu_dump_t *) BigBuf_get_EM_addr();
*pages = MAX(mfu_header->pages, 15);
// counters and tearing flags
// tearing flags
// for old dumps with all zero headers, we need to set default values.
for (uint8_t i = 0; i < 3; i++) {
counters[i] = le24toh(mfu_header->counter_tearing[i]);
if (mfu_header->counter_tearing[i][3] != 0x00) {
tearings[i] = mfu_header->counter_tearing[i][3];
if (mfu_header->counter_tearing[i][3] == 0x00) {
mfu_header->counter_tearing[i][3] = 0xBD;
}
}
@@ -1286,14 +1283,11 @@ bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data,
mfu_dump_t *mfu_header = (mfu_dump_t *) BigBuf_get_EM_addr();
*pages = MAX(mfu_header->pages, 19);
// counters and tearing flags
// tearing flags
// for old dumps with all zero headers, we need to set default values.
for (uint8_t i = 0; i < 3; i++) {
counters[i] = le24toh(mfu_header->counter_tearing[i]);
if (mfu_header->counter_tearing[i][3] != 0x00) {
tearings[i] = mfu_header->counter_tearing[i][3];
if (mfu_header->counter_tearing[i][3] == 0x00) {
mfu_header->counter_tearing[i][3] = 0xBD;
}
}
@@ -1539,8 +1533,6 @@ void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *useruid, uin
tag_response_info_t *responses;
uint32_t cuid = 0;
uint32_t nonce = 0;
uint32_t counters[3] = { 0x00, 0x00, 0x00 };
uint8_t tearings[3] = { 0xbd, 0xbd, 0xbd };
uint8_t pages = 0;
// Here, we collect CUID, block1, keytype1, NT1, NR1, AR1, CUID, block2, keytyp2, NT2, NR2, AR2
@@ -1584,12 +1576,22 @@ 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, counters, tearings, &pages) == false) {
if (SimulateIso14443aInit(tagType, flags, useruid, ats, ats_len, &responses, &cuid, &pages) == false) {
BigBuf_free_keep_EM();
reply_ng(CMD_HF_MIFARE_SIMULATE, PM3_EINIT, NULL, 0);
return;
}
mfu_dump_t *mfu_em_dump = NULL;
if (tagType == 2 || tagType == 7) {
mfu_em_dump = (mfu_dump_t *)BigBuf_get_EM_addr();
if (!mfu_em_dump) {
if (g_dbglevel >= DBG_ERROR) Dbprintf("[-] ERROR: Failed to get EM address for MFU/NTAG operations.");
reply_ng(CMD_HF_MIFARE_SIMULATE, PM3_EMALLOC, NULL, 0);
return;
}
}
// We need to listen to the high-frequency, peak-detected path.
iso14443a_setup(FPGA_HF_ISO14443A_TAGSIM_LISTEN);
@@ -1870,8 +1872,8 @@ void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *useruid, uin
// send NACK 0x0 == invalid argument
EmSend4bit(CARD_NACK_IV);
} else {
uint8_t cmd[] = {0x00, 0x00, 0x00, 0x14, 0xa5};
htole24(counters[index], cmd);
uint8_t cmd[] = {0, 0, 0, 0x14, 0xa5};
memcpy(cmd, mfu_em_dump->counter_tearing[index], 3);
AddCrc14A(cmd, sizeof(cmd) - 2);
EmSendCmd(cmd, sizeof(cmd));
}
@@ -1882,13 +1884,16 @@ void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *useruid, uin
// send NACK 0x0 == invalid argument
EmSend4bit(CARD_NACK_IV);
} else {
uint32_t val = le24toh(receivedCmd + 2) + counters[index];
uint32_t val = le24toh(mfu_em_dump->counter_tearing[index]); // get current counter value
val += le24toh(receivedCmd + 2); // increment in
// if new value + old value is bigger 24bits, fail
if (val > 0xFFFFFF) {
// send NACK 0x4 == counter overflow
EmSend4bit(CARD_NACK_NA);
} else {
counters[index] = val;
htole24(val, mfu_em_dump->counter_tearing[index]);
// send ACK
EmSend4bit(CARD_ACK);
}
@@ -1902,7 +1907,7 @@ void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *useruid, uin
EmSend4bit(CARD_NACK_IV);
} else {
uint8_t cmd[3] = {0, 0, 0};
cmd[0] = tearings[index];
cmd[0] = mfu_em_dump->counter_tearing[index][3];
AddCrc14A(cmd, sizeof(cmd) - 2);
EmSendCmd(cmd, sizeof(cmd));
}
@@ -4091,8 +4096,6 @@ void SimulateIso14443aTagAID(uint8_t tagType, uint16_t flags, uint8_t *uid,
uint8_t *getdata_response, size_t getdata_response_len) {
tag_response_info_t *responses;
uint32_t cuid = 0;
uint32_t counters[3] = { 0x00, 0x00, 0x00 };
uint8_t tearings[3] = { 0xbd, 0xbd, 0xbd };
uint8_t pages = 0;
// command buffers
@@ -4133,7 +4136,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, counters, tearings, &pages) == false) {
if (SimulateIso14443aInit(tagType, flags, uid, ats, ats_len, &responses, &cuid, &pages) == false) {
BigBuf_free_keep_EM();
reply_ng(CMD_HF_MIFARE_SIMULATE, PM3_EINIT, NULL, 0);
return;
@@ -4276,6 +4279,7 @@ void SimulateIso14443aTagAID(uint8_t tagType, uint16_t flags, uint8_t *uid,
dynamic_response_info.response_n = 3 + offset;
}
}
break;
}
break;
+1 -1
View File
@@ -152,7 +152,7 @@ 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, uint32_t counters[3], uint8_t tearings[3], uint8_t *pages);
uint32_t *cuid, uint8_t *pages);
bool GetIso14443aCommandFromReader(uint8_t *received, uint16_t received_maxlen, uint8_t *par, int *len);
void iso14443a_antifuzz(uint32_t flags);
+3
View File
@@ -2135,6 +2135,9 @@ static int iso14443b_select_picopass_card(picopass_hdr_t *hdr) {
static uint8_t act_all[] = { ICLASS_CMD_ACTALL };
static uint8_t identify[] = { ICLASS_CMD_READ_OR_IDENTIFY };
static uint8_t read_conf[] = { ICLASS_CMD_READ_OR_IDENTIFY, 0x01, 0xfa, 0x22 };
// ICLASS_CMD_SELECT 0x81 tells ISO14443b/BPSK coding/106 kbits/s
// ICLASS_CMD_SELECT 0x41 tells ISO14443b/BPSK coding/423 kbits/s
uint8_t select[] = { 0x80 | ICLASS_CMD_SELECT, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
uint8_t read_aia[] = { ICLASS_CMD_READ_OR_IDENTIFY, 0x05, 0xde, 0x64};
uint8_t read_check_cc[] = { 0x80 | ICLASS_CMD_READCHECK, 0x02 };
+132 -24
View File
@@ -1827,14 +1827,17 @@ static int detect_nxp_card_print(uint8_t sak, uint16_t atqa, uint64_t select_sta
const char *product_type_str = "";
const char *major_product_version_str = "";
const char *storage_size_str = "";
if (version_hw_available) {
switch (version_hw->product_type & 0x0F) {
case 0x1:
case 0x1: {
product_type_str = "MIFARE DESFire";
// special cases, override product_type_str when needed
if (version_hw->product_type == 0x91) {
product_type_str = "Apple Wallet DESFire Applet";
}
// general rule
switch (version_hw->major_product_version & 0x0F) {
case 0x01:
@@ -1847,6 +1850,7 @@ static int detect_nxp_card_print(uint8_t sak, uint16_t atqa, uint64_t select_sta
major_product_version_str = "EV3";
break;
}
// special cases, override major_product_version_str when needed
switch (version_hw->major_product_version) {
case 0x00:
@@ -1860,7 +1864,8 @@ static int detect_nxp_card_print(uint8_t sak, uint16_t atqa, uint64_t select_sta
break;
}
break;
case 0x2:
}
case 0x2: {
product_type_str = "MIFARE Plus";
switch (version_hw->major_product_version) {
case 0x11:
@@ -1870,15 +1875,23 @@ static int detect_nxp_card_print(uint8_t sak, uint16_t atqa, uint64_t select_sta
major_product_version_str = "EV2";
break;
default:
major_product_version_str = "Unknown";
major_product_version_str = "n/a";
}
break;
case 0x3:
}
case 0x3: {
product_type_str = "MIFARE Ultralight";
switch (version_hw->major_product_version) {
case 0x01:
case 0x01: {
major_product_version_str = "EV1";
if (version_hw->storage_size == 0x0B) {
storage_size_str = "48b";
} else if (version_hw->storage_size == 0x0E) {
storage_size_str = "128b";
}
break;
}
case 0x02:
major_product_version_str = "Nano";
break;
@@ -1886,10 +1899,11 @@ static int detect_nxp_card_print(uint8_t sak, uint16_t atqa, uint64_t select_sta
major_product_version_str = "AES";
break;
default:
major_product_version_str = "Unknown";
major_product_version_str = "n/a";
}
break;
case 0x4:
}
case 0x4: {
product_type_str = "NTAG";
switch (version_hw->major_product_version) {
case 0x01:
@@ -1909,37 +1923,66 @@ static int detect_nxp_card_print(uint8_t sak, uint16_t atqa, uint64_t select_sta
major_product_version_str = "4xx";
break;
default:
major_product_version_str = "Unknown";
major_product_version_str = "n/a";
}
break;
case 0x7:
}
case 0x7: {
product_type_str = "NTAG I2C";
break;
case 0x8:
}
case 0x8: {
product_type_str = "MIFARE DESFire Light";
break;
case 0x9:
}
case 0x9: {
product_type_str = "MIFARE Hospitality";
switch (version_hw->major_product_version) {
case 0x01:
major_product_version_str = "AES";
break;
default:
major_product_version_str = "Unknown";
major_product_version_str = "n/a";
}
break;
default:
}
default: {
product_type_str = "Unknown NXP tag";
break;
}
}
uint32_t size = 1 << (version_hw->storage_size >> 1);
static char size_str[16];
if (size < 1024) {
snprintf(size_str, sizeof(size_str), "%s%uB", (version_hw->storage_size & 0x01) == 0 ? "" : "~", size);
} else {
snprintf(size_str, sizeof(size_str), "%s%uK", (version_hw->storage_size & 0x01) == 0 ? "" : "~", size / 1024);
if (storage_size_str == NULL) {
static char size_str[16];
uint16_t usize = 1 << ((version_hw->storage_size >> 1) + 1);
uint16_t lsize = 1 << (version_hw->storage_size >> 1);
// is LSB set?
if ((version_hw->storage_size & 0x01) == 1) {
// if set, its a range between upper size and lower size
if (lsize < 1024) {
snprintf(size_str, sizeof(size_str), "%u - %uB", usize, lsize);
} else {
snprintf(size_str, sizeof(size_str), "%u - %uK", (usize / 1024), (lsize / 1024));
}
} else {
// if not set, it's lower size
if (lsize < 1024) {
snprintf(size_str, sizeof(size_str), "%uB", lsize);
} else {
snprintf(size_str, sizeof(size_str), "%uK", lsize / 1024);
}
}
storage_size_str = size_str;
}
storage_size_str = size_str;
}
char tag_info[128];
if ((sak & 0x44) == 0x40) {
@@ -1951,25 +1994,36 @@ static int detect_nxp_card_print(uint8_t sak, uint16_t atqa, uint64_t select_sta
}
type |= MTISO18092;
}
if ((sak & 0x02) == 0x00) { // SAK b2=0
if ((sak & 0x08) == 0x08) { // SAK b2=0 b4=1
if ((sak & 0x10) == 0x10) { // SAK b2=0 b4=1 b5=1
if ((sak & 0x01) == 0x01) { // SAK b2=0 b4=1 b5=1 b1=1, SAK=0x19
printTag("MIFARE Classic 2K");
type |= MTCLASSIC;
} else { // SAK b2=0 b4=1 b5=1 b1=0
if ((sak & 0x20) == 0x20) { // SAK b2=0 b4=1 b5=1 b1=0 b6=1, SAK=0x38
printTag("SmartMX with MIFARE Classic 4K");
type |= MTCLASSIC;
} else { // SAK b2=0 b4=1 b5=1 b1=0 b6=0
if (select_status == 4) { // SAK b2=0 b4=1 b5=1 b1=0 b6=0 ATS
if (version_hw_available) { // SAK b2=0 b4=1 b5=1 b1=0 b6=0 ATS GetVersion
snprintf(tag_info, sizeof(tag_info), "%s %s %s in SL1", product_type_str, major_product_version_str, storage_size_str);
printTag(tag_info);
type |= MTPLUS;
} else { // SAK b2=0 b4=1 b5=1 b1=0 b6=0 ATS No_GetVersion
if (ats_hist_len > 0) {
if ((ats_hist_len == 9) && (memcmp(ats_hist, "\xC1\x05\x2F\x2F", 4) == 0)) {
if (memcmp(ats_hist + 4, "\x00\x35\xC7", 3) == 0) {
printTag("MIFARE Plus S 4K in SL1");
} else if (memcmp(ats_hist + 4, "\x01\xBC\xD6", 3) == 0) {
@@ -1988,7 +2042,9 @@ static int detect_nxp_card_print(uint8_t sak, uint16_t atqa, uint64_t select_sta
}
}
}
} else { // SAK b2=0 b4=1 b5=1 b1=0 b6=0 no_ATS, SAK=0x18
if ((atqa & 0x0040) == 0x0040) {
printTag("MIFARE Classic 4K CL2");
} else {
@@ -2000,28 +2056,38 @@ static int detect_nxp_card_print(uint8_t sak, uint16_t atqa, uint64_t select_sta
}
} else { // SAK b2=0 b4=1 b5=0
if ((sak & 0x01) == 0x01) { // SAK b2=0 b4=1 b5=0 b1=1, SAK=0x09
if ((atqa & 0x0040) == 0x0040) {
printTag("MIFARE Mini 0.3K CL2");
} else {
printTag("MIFARE Mini 0.3K");
}
type |= MTMINI;
} else { // SAK b2=0 b4=1 b5=0 b1=0
if ((sak & 0x20) == 0x20) { // SAK b2=0 b4=1 b5=0 b1=0 b6=1, SAK=0x28
printTag("SmartMX with MIFARE Classic 1K");
printTag("FM1208-10 with MIFARE Classic 1K");
printTag("FM1216-137 with MIFARE Classic 1K");
type |= MTCLASSIC;
} else { // SAK b2=0 b4=1 b5=0 b1=0 b6=0
if (select_status == 4) { // SAK b2=0 b4=1 b5=0 b1=0 b6=0 ATS
if (version_hw_available) { // SAK b2=0 b4=1 b5=0 b1=0 b6=0 ATS GetVersion
snprintf(tag_info, sizeof(tag_info), "%s %s %s in SL1", product_type_str, major_product_version_str, storage_size_str);
printTag(tag_info);
type |= MTPLUS;
} else { // SAK b2=0 b4=1 b5=0 b1=0 b6=0 ATS No_GetVersion
if (ats_hist_len > 0) {
if ((ats_hist_len == 9) && (memcmp(ats_hist, "\xC1\x05\x2F\x2F", 4) == 0)) {
if (memcmp(ats_hist + 4, "\x00\x35\xC7", 3) == 0) {
printTag("MIFARE Plus S 2K in SL1");
} else if (memcmp(ats_hist + 4, "\x01\xBC\xD6", 3) == 0) {
@@ -2030,7 +2096,9 @@ static int detect_nxp_card_print(uint8_t sak, uint16_t atqa, uint64_t select_sta
printTag("Unrecognized MIFARE Plus??");
}
type |= MTPLUS;
} else if ((ats_hist_len == 9) && (memcmp(ats_hist, "\xC1\x05\x21\x30", 4) == 0)) {
if (memcmp(ats_hist + 4, "\x00\xF6\xD1", 3) == 0) {
printTag("MIFARE Plus SE 1K 17pF");
} else if (memcmp(ats_hist + 4, "\x10\xF6\xD1", 3) == 0) {
@@ -2039,7 +2107,9 @@ static int detect_nxp_card_print(uint8_t sak, uint16_t atqa, uint64_t select_sta
printTag("Unrecognized MIFARE Plus SE??");
}
type |= MTPLUS;
} else {
if ((atqa & 0x0040) == 0x0040) {
printTag("MIFARE Classic 1K CL2 with ATS!");
} else {
@@ -2060,8 +2130,11 @@ static int detect_nxp_card_print(uint8_t sak, uint16_t atqa, uint64_t select_sta
}
}
}
} else { // SAK b2=0 b4=0
if ((sak & 0x10) == 0x10) { // SAK b2=0 b4=0 b5=1
if ((sak & 0x01) == 0x01) { // SAK b2=0 b4=0 b5=1 b1=1, SAK=0x11
printTag("MIFARE Plus 4K in SL2");
type |= MTPLUS;
@@ -2069,33 +2142,48 @@ static int detect_nxp_card_print(uint8_t sak, uint16_t atqa, uint64_t select_sta
printTag("MIFARE Plus 2K in SL2");
type |= MTPLUS;
}
} else { // SAK b2=0 b4=0 b5=0
if ((sak & 0x01) == 0x01) { // SAK b2=0 b4=0 b5=0 b1=1
printTag("TNP3xxx (TagNPlay, Activision Game Appliance)");
type |= MTCLASSIC;
} else { // SAK b2=0 b4=0 b5=0 b1=0
if ((sak & 0x20) == 0x20) { // SAK b2=0 b4=0 b5=0 b1=0 b6=1, SAK=0x20
if (select_status == 1) { // SAK b2=0 b4=0 b5=0 b1=0 b6=1 ATS
if (version_hw_available) { // SAK b2=0 b4=0 b5=0 b1=0 b6=1 ATS GetVersion
if ((version_hw->product_type & 0x7F) == 0x02) {
snprintf(tag_info, sizeof(tag_info), "%s %s %s in SL0/SL3", product_type_str, major_product_version_str, storage_size_str);
type |= MTPLUS;
} else if (((version_hw->product_type & 0x7F) == 0x01) ||
(version_hw->product_type == 0x08) ||
(version_hw->product_type == 0x91)) {
snprintf(tag_info, sizeof(tag_info), "%s %s %s", product_type_str, major_product_version_str, storage_size_str);
type |= MTDESFIRE;
} else if (version_hw->product_type == 0x04) {
snprintf(tag_info, sizeof(tag_info), "%s %s %s", product_type_str, major_product_version_str, storage_size_str);
type |= (MTDESFIRE | MT424);
} else {
snprintf(tag_info, sizeof(tag_info), "%s %s %s", product_type_str, major_product_version_str, storage_size_str);
}
printTag(tag_info);
} else { // SAK b2=0 b4=0 b5=0 b1=0 b6=1 ATS No GetVersion
if (ats_hist_len > 0) {
if ((ats_hist_len == 9) && (memcmp(ats_hist, "\xC1\x05\x2F\x2F", 4) == 0)) {
if (memcmp(ats_hist + 4, "\x00\x35\xC7", 3) == 0) {
if ((atqa & 0xFF0F) == 0x0004) {
printTag("MIFARE Plus S 2K in SL0/SL3");
} else if ((atqa & 0xFF0F) == 0x0002) {
@@ -2103,21 +2191,28 @@ static int detect_nxp_card_print(uint8_t sak, uint16_t atqa, uint64_t select_sta
} else {
printTag("Unrecognized MIFARE Plus??");
}
} else if (memcmp(ats_hist + 4, "\x01\xBC\xD6", 3) == 0) {
printTag("MIFARE Plus X 2K/4K in SL0/SL3");
} else if (memcmp(ats_hist + 4, "\x00\xF6\xD1", 3) == 0) {
printTag("MIFARE Plus SE 1K 17pF");
} else if (memcmp(ats_hist + 4, "\x10\xF6\xD1", 3) == 0) {
printTag("MIFARE Plus SE 1K 70pF");
} else {
printTag("Unrecognized MIFARE Plus??");
printTag("Unknown MIFARE Plus");
}
type |= MTPLUS;
} else {
if ((atqa == 0x0001) || (atqa == 0x0004)) {
printTag("HID SEOS (smartmx / javacard)");
type |= HID_SEOS;
}
if (atqa == 0x0004) {
printTag("EMV");
type |= MTEMV;
@@ -2128,11 +2223,15 @@ static int detect_nxp_card_print(uint8_t sak, uint16_t atqa, uint64_t select_sta
} else {
printTag("Unknown tag claims to support RATS in SAK but does not...");
}
} else { // SAK b2=0 b4=0 b5=0 b1=0 b6=0, SAK=0x00
if (version_hw_available) { // SAK b2=0 b4=0 b5=0 b1=0 b6=0 GetVersion
snprintf(tag_info, sizeof(tag_info), "%s %s %s", product_type_str, major_product_version_str, storage_size_str);
printTag(tag_info);
} else { // SAK b2=0 b4=0 b5=0 b1=0 b6=0 No_GetVersion
int status = mfuc_test_authentication_support();
if (status == PM3_SUCCESS) {
// TODO: read page 2/3, then ??
@@ -2141,6 +2240,7 @@ static int detect_nxp_card_print(uint8_t sak, uint16_t atqa, uint64_t select_sta
} else {
printTag("MIFARE Ultralight");
}
}
type |= MTULTRALIGHT;
}
@@ -2148,20 +2248,25 @@ static int detect_nxp_card_print(uint8_t sak, uint16_t atqa, uint64_t select_sta
}
}
} else { // SAK b2=1
if (sak == 0x0A) {
if (atqa == 0x0003) {
// Uses Shanghai algo
printTag("FM11RF005SH (FUDAN Shanghai Metro)");
type |= MTFUDAN;
} else if (atqa == 0x0005) {
printTag("FM11RF005M (FUDAN ISO14443A w Crypto-1 algo)");
type |= MTFUDAN;
}
} else if (sak == 0x53) {
printTag("FM11RF08SH (FUDAN)");
type |= MTFUDAN;
}
}
if (type == MTNONE) {
PrintAndLogEx(WARNING, " failed to fingerprint");
}
@@ -2803,8 +2908,9 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) {
}
DropField();
if (verbose == false && found)
if (verbose == false && found) {
PrintAndLogEx(INFO, "----------------------------------------------------");
}
}
}
@@ -2814,7 +2920,9 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) {
PrintAndLogEx(INFO, "proprietary iso18092 card found");
} else {
PrintAndLogEx(INFO, "proprietary non iso14443-4 card found, RATS not supported");
PrintAndLogEx(INFO, "");
PrintAndLogEx(INFO, "Proprietary non iso14443-4 card found");
PrintAndLogEx(INFO, "RATS not supported");
if ((card.sak & 0x20) == 0x20) {
PrintAndLogEx(INFO, "--> SAK incorrectly claims that card supports RATS <--");
}
@@ -2829,7 +2937,7 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) {
return PM3_EFAILED;
}
PrintAndLogEx(INFO, "");
// PrintAndLogEx(INFO, "");
uint16_t isMagic = 0;
+4 -7
View File
@@ -1411,7 +1411,7 @@ static bool HF14B_ask_ct_reader(bool verbose) {
return false;
}
bool HF14B_picopass_reader(bool verbose, bool info) {
static bool HF14B_picopass_reader(bool verbose) {
iso14b_raw_cmd_t packet = {
.flags = (ISO14B_CONNECT | ISO14B_SELECT_PICOPASS | ISO14B_DISCONNECT),
@@ -1437,10 +1437,8 @@ bool HF14B_picopass_reader(bool verbose, bool info) {
return false;
}
memcpy(card, resp.data.asBytes, sizeof(picopass_hdr_t));
if (info) {
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(SUCCESS, "iCLASS / Picopass CSN: " _GREEN_("%s"), sprint_hex(card->csn, sizeof(card->csn)));
}
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(SUCCESS, "iCLASS / Picopass CSN: " _GREEN_("%s"), sprint_hex(card->csn, sizeof(card->csn)));
free(card);
return true;
}
@@ -3038,7 +3036,6 @@ int infoHF14B(bool verbose, bool do_aid_search) {
// get and print general info about all known 14b chips
int readHF14B(bool loop, bool verbose, bool read_plot) {
bool found = false;
bool info = true;
int res = PM3_SUCCESS;
do {
found = false;
@@ -3054,7 +3051,7 @@ int readHF14B(bool loop, bool verbose, bool read_plot) {
goto plot;
// Picopass
found |= HF14B_picopass_reader(verbose, info);
found |= HF14B_picopass_reader(verbose);
if (found)
goto plot;
-1
View File
@@ -31,6 +31,5 @@ int select_card_14443b_4(bool disconnect, iso14b_card_select_t *card);
int infoHF14B(bool verbose, bool do_aid_search);
int readHF14B(bool loop, bool verbose, bool read_plot);
bool HF14B_picopass_reader(bool verbose, bool info);
#endif
+280 -129
View File
File diff suppressed because it is too large Load Diff
+359 -3
View File
@@ -48,6 +48,301 @@
#include "mifare/mifarehost.h"
#include "crypto/originality.h"
// Defines for Saflok parsing
#define SAFLOK_YEAR_OFFSET 1980
#define SAFLOK_BASIC_ACCESS_BYTE_NUM 17
#define SAFLOK_KEY_LENGTH 6
#define SAFLOK_UID_LENGTH 4 // Matches Mifare 4-byte UID
#define SAFLOK_MAGIC_TABLE_SIZE 192
#define SAFLOK_CHECK_SECTOR 1
typedef struct {
uint64_t a;
uint64_t b;
} MfClassicKeyPair;
// Structure for Saflok key levels
typedef struct {
uint8_t level_num;
const char *level_name;
} SaflokKeyLevel;
// Static array for Saflok key levels
static const SaflokKeyLevel saflok_key_levels[] = {
{1, "Guest Key"},
{2, "Connectors"},
{3, "Suite"},
{4, "Limited Use"},
{5, "Failsafe"},
{6, "Inhibit"},
{7, "Pool/Meeting Master"},
{8, "Housekeeping"},
{9, "Floor Key"},
{10, "Section Key"},
{11, "Rooms Master"},
{12, "Grand Master"},
{13, "Emergency"},
{14, "Electronic Lockout"},
{15, "Secondary Programming Key (SPK)"},
{16, "Primary Programming Key (PPK)"},
};
// Lookup table for Saflok decryption
static const uint8_t saflok_c_aDecode[256] = {
0xEA, 0x0D, 0xD9, 0x74, 0x4E, 0x28, 0xFD, 0xBA, 0x7B, 0x98, 0x87, 0x78, 0xDD, 0x8D, 0xB5,
0x1A, 0x0E, 0x30, 0xF3, 0x2F, 0x6A, 0x3B, 0xAC, 0x09, 0xB9, 0x20, 0x6E, 0x5B, 0x2B, 0xB6,
0x21, 0xAA, 0x17, 0x44, 0x5A, 0x54, 0x57, 0xBE, 0x0A, 0x52, 0x67, 0xC9, 0x50, 0x35, 0xF5,
0x41, 0xA0, 0x94, 0x60, 0xFE, 0x24, 0xA2, 0x36, 0xEF, 0x1E, 0x6B, 0xF7, 0x9C, 0x69, 0xDA,
0x9B, 0x6F, 0xAD, 0xD8, 0xFB, 0x97, 0x62, 0x5F, 0x1F, 0x38, 0xC2, 0xD7, 0x71, 0x31, 0xF0,
0x13, 0xEE, 0x0F, 0xA3, 0xA7, 0x1C, 0xD5, 0x11, 0x4C, 0x45, 0x2C, 0x04, 0xDB, 0xA6, 0x2E,
0xF8, 0x64, 0x9A, 0xB8, 0x53, 0x66, 0xDC, 0x7A, 0x5D, 0x03, 0x07, 0x80, 0x37, 0xFF, 0xFC,
0x06, 0xBC, 0x26, 0xC0, 0x95, 0x4A, 0xF1, 0x51, 0x2D, 0x22, 0x18, 0x01, 0x79, 0x5E, 0x76,
0x1D, 0x7F, 0x14, 0xE3, 0x9E, 0x8A, 0xBB, 0x34, 0xBF, 0xF4, 0xAB, 0x48, 0x63, 0x55, 0x3E,
0x56, 0x8C, 0xD1, 0x12, 0xED, 0xC3, 0x49, 0x8E, 0x92, 0x9D, 0xCA, 0xB1, 0xE5, 0xCE, 0x4D,
0x3F, 0xFA, 0x73, 0x05, 0xE0, 0x4B, 0x93, 0xB2, 0xCB, 0x08, 0xE1, 0x96, 0x19, 0x3D, 0x83,
0x39, 0x75, 0xEC, 0xD6, 0x3C, 0xD0, 0x70, 0x81, 0x16, 0x29, 0x15, 0x6C, 0xC7, 0xE7, 0xE2,
0xF6, 0xB7, 0xE8, 0x25, 0x6D, 0x3A, 0xE6, 0xC8, 0x99, 0x46, 0xB0, 0x85, 0x02, 0x61, 0x1B,
0x8B, 0xB3, 0x9F, 0x0B, 0x2A, 0xA8, 0x77, 0x10, 0xC1, 0x88, 0xCC, 0xA4, 0xDE, 0x43, 0x58,
0x23, 0xB4, 0xA1, 0xA5, 0x5C, 0xAE, 0xA9, 0x7E, 0x42, 0x40, 0x90, 0xD2, 0xE9, 0x84, 0xCF,
0xE4, 0xEB, 0x47, 0x4F, 0x82, 0xD4, 0xC5, 0x8F, 0xCD, 0xD3, 0x86, 0x00, 0x59, 0xDF, 0xF2,
0x0C, 0x7C, 0xC6, 0xBD, 0xF9, 0x7D, 0xC4, 0x91, 0x27, 0x89, 0x32, 0x72, 0x33, 0x65, 0x68,
0xAF
};
// Function to decrypt Saflok card data
static void DecryptSaflokCardData(
const uint8_t strCard[SAFLOK_BASIC_ACCESS_BYTE_NUM],
// int length, // length is always SAFLOK_BASIC_ACCESS_BYTE_NUM
uint8_t decryptedCard[SAFLOK_BASIC_ACCESS_BYTE_NUM]) {
int i;
int num;
int num2;
int num3;
int num4;
int b = 0;
int b2 = 0;
for (i = 0; i < SAFLOK_BASIC_ACCESS_BYTE_NUM; i++) {
num = saflok_c_aDecode[strCard[i]] - (i + 1);
if (num < 0) num += 256;
decryptedCard[i] = num;
}
b = decryptedCard[10];
b2 = b & 1;
for (num2 = SAFLOK_BASIC_ACCESS_BYTE_NUM; num2 > 0; num2--) {
b = decryptedCard[num2 - 1];
for (num3 = 8; num3 > 0; num3--) {
num4 = num2 + num3;
if (num4 > SAFLOK_BASIC_ACCESS_BYTE_NUM) num4 -= SAFLOK_BASIC_ACCESS_BYTE_NUM;
int b3 = decryptedCard[num4 - 1];
int b4 = (b3 & 0x80) >> 7;
b3 = ((b3 << 1) & 0xFF) | b2;
b2 = (b & 0x80) >> 7;
b = ((b << 1) & 0xFF) | b4;
decryptedCard[num4 - 1] = b3;
}
decryptedCard[num2 - 1] = b;
}
}
// Function to calculate Saflok checksum
static uint8_t CalculateCheckSum(uint8_t data[SAFLOK_BASIC_ACCESS_BYTE_NUM]) {
int sum = 0;
for (int i = 0; i < SAFLOK_BASIC_ACCESS_BYTE_NUM - 1; i++) {
sum += data[i];
}
sum = 255 - (sum & 0xFF);
return sum & 0xFF;
}
// Function to parse and print Saflok data
static void ParseAndPrintSaflokData(const sector_t *sector0_info, const sector_t *sector1_info) {
(void)sector1_info; // Not directly used for payload parsing currently
if (!sector0_info) {
PrintAndLogEx(WARNING, "Saflok: Sector 0 information not available for parsing.");
return;
}
uint8_t key_bytes_for_s0[MIFARE_KEY_SIZE];
uint8_t key_type_for_s0; // CORRECTED: Was MifareKeyType, now uint8_t
bool s0_key_found = false;
// Prioritize Key A for Sector 0 if available
if (sector0_info->foundKey[MF_KEY_A]) {
num_to_bytes(sector0_info->Key[MF_KEY_A], MIFARE_KEY_SIZE, key_bytes_for_s0);
key_type_for_s0 = MF_KEY_A; // MF_KEY_A is typically #define'd as 0x60
s0_key_found = true;
PrintAndLogEx(DEBUG, "Saflok: Using Sector 0 Key A for reading blocks.");
} else if (sector0_info->foundKey[MF_KEY_B]) { // Fallback to Key B for Sector 0
num_to_bytes(sector0_info->Key[MF_KEY_B], MIFARE_KEY_SIZE, key_bytes_for_s0);
key_type_for_s0 = MF_KEY_B; // MF_KEY_B is typically #define'd as 0x61
s0_key_found = true;
PrintAndLogEx(DEBUG, "Saflok: Using Sector 0 Key B for reading blocks.");
}
if (!s0_key_found) {
PrintAndLogEx(WARNING, "Saflok: No known keys for Sector 0. Cannot read blocks 1 & 2 for parsing.");
return;
}
uint8_t block1_content[MFBLOCK_SIZE];
uint8_t block2_content[MFBLOCK_SIZE];
// Read absolute block 1 (data block within sector 0)
if (mf_read_block(1, key_type_for_s0, key_bytes_for_s0, block1_content) != PM3_SUCCESS) {
PrintAndLogEx(WARNING, "Saflok: Failed to read card Block 1 using Sector 0 %s key.", (key_type_for_s0 == MF_KEY_A) ? "A" : "B");
return;
}
PrintAndLogEx(DEBUG, "Saflok: Successfully read card Block 1.");
// Read absolute block 2 (data block within sector 0)
if (mf_read_block(2, key_type_for_s0, key_bytes_for_s0, block2_content) != PM3_SUCCESS) {
PrintAndLogEx(WARNING, "Saflok: Failed to read card Block 2 using Sector 0 %s key.", (key_type_for_s0 == MF_KEY_A) ? "A" : "B");
return;
}
PrintAndLogEx(DEBUG, "Saflok: Successfully read card Block 2.");
uint8_t basicAccess[SAFLOK_BASIC_ACCESS_BYTE_NUM];
uint8_t decodedBA[SAFLOK_BASIC_ACCESS_BYTE_NUM];
memcpy(basicAccess, block1_content, 16); // 16 bytes from Block 1
memcpy(basicAccess + 16, block2_content, 1); // 1 byte from Block 2
DecryptSaflokCardData(basicAccess, decodedBA);
// Byte 0: Key level, LED warning bit, and subgroup functions
uint8_t key_level = (decodedBA[0] & 0xF0) >> 4;
uint8_t led_warning = (decodedBA[0] & 0x08) >> 3;
// Byte 1: Key ID
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];
// Byte 5 & 6: EncryptSequence + Combination
uint16_t sequence_combination_number = ((decodedBA[5] & 0x0F) << 8) | decodedBA[6];
// Byte 7: OverrideDeadbolt and Days
uint8_t override_deadbolt = (decodedBA[7] & 0x80) >> 7;
uint8_t restricted_weekday = decodedBA[7] & 0x7F;
// Weekday names array
static const char *weekdays[] = {"Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"};
// Buffer to store the resulting string (sufficient size for all weekdays)
char restricted_weekday_string[128] = {0};
int restricted_count = 0;
// Check each bit from Monday to Sunday
for (int i = 0; i < 7; i++) {
if (restricted_weekday & (0b01000000 >> i)) {
// If the bit is set, append the corresponding weekday to the buffer
if (restricted_count > 0) {
strcat(restricted_weekday_string, ", ");
}
strcat(restricted_weekday_string, weekdays[i]);
restricted_count++;
}
}
// Determine if all weekdays are restricted
if (restricted_weekday == 0b01111100) {
strcpy(restricted_weekday_string, "weekdays");
}
// If there are specific restricted days
else if (restricted_weekday == 0b00000011) {
strcpy(restricted_weekday_string, "weekends");
}
// If no weekdays are restricted
else if (restricted_weekday == 0) {
strcpy(restricted_weekday_string, "none");
}
// Bytes 14-15: Property number and part of creation year
uint8_t creation_year_high_bits = (decodedBA[14] & 0xF0);
uint16_t property_id = ((decodedBA[14] & 0x0F) << 8) | decodedBA[15];
// Bytes 11-13: Creation date since SAFLOK_YEAR_OFFSET Jan 1st
uint16_t creation_year = (((decodedBA[11] & 0xF0) >> 4) + SAFLOK_YEAR_OFFSET) | creation_year_high_bits;
uint8_t creation_month = decodedBA[11] & 0x0F;
uint8_t creation_day = (decodedBA[12] >> 3) & 0x1F;
uint8_t creation_hour = ((decodedBA[12] & 0x07) << 2) | ((decodedBA[13] & 0xC0) >> 6);
uint8_t creation_minute = decodedBA[13] & 0x3F;
// Bytes 8-10: Expiry interval / absolute time components
uint8_t interval_year_val = (decodedBA[8] >> 4);
uint8_t interval_month_val = decodedBA[8] & 0x0F;
uint8_t interval_day_val = (decodedBA[9] >> 3) & 0x1F;
uint8_t expiry_hour = ((decodedBA[9] & 0x07) << 2) | ((decodedBA[10] & 0xC0) >> 6);
uint8_t expiry_minute = decodedBA[10] & 0x3F;
uint16_t expire_year = creation_year + interval_year_val;
uint8_t expire_month = creation_month + interval_month_val;
uint8_t expire_day = creation_day + interval_day_val;
// Handle month rollover for expiration
while (expire_month > 12) {
expire_month -= 12;
expire_year++;
}
// Handle day rollover for expiration
static const uint8_t days_in_month_lookup[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; // 1-indexed month
if (expire_month > 0 && expire_month <= 12) {
while (true) {
uint8_t max_days = days_in_month_lookup[expire_month];
if (expire_month == 2 && (expire_year % 4 == 0 && (expire_year % 100 != 0 || expire_year % 400 == 0))) {
max_days = 29; // Leap year
}
if (expire_day <= max_days) {
break;
}
if (max_days == 0) { // Should not happen with valid month
PrintAndLogEx(WARNING, "Saflok: Invalid day/month for expiration rollover calculation.");
break;
}
expire_day -= max_days;
expire_month++;
if (expire_month > 12) {
expire_month = 1;
expire_year++;
}
}
} else if (expire_month != 0) { // Allow 0 if it signifies no expiration or error
PrintAndLogEx(WARNING, "Saflok: Invalid expiration month (%u) before day rollover.", expire_month);
}
uint8_t checksum = decodedBA[16];
uint8_t checksum_calculated = CalculateCheckSum(decodedBA);
bool checksum_valid = (checksum_calculated == checksum);
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, "--- " _CYAN_("Saflok Details"));
PrintAndLogEx(SUCCESS, "Key Level: %u (%s)", saflok_key_levels[key_level].level_num, saflok_key_levels[key_level].level_name);
PrintAndLogEx(SUCCESS, "LED Warning: %s", led_warning ? "Yes" : "No");
PrintAndLogEx(SUCCESS, "Key ID: %u (0x%02X)", key_id, key_id);
PrintAndLogEx(SUCCESS, "Key Record: %u (0x%04X)", key_record, key_record);
PrintAndLogEx(SUCCESS, "Opening Key: %s", opening_key ? "Yes" : "No");
PrintAndLogEx(SUCCESS, "Sequence Number & Combination: %u (0x%02X)", sequence_combination_number, sequence_combination_number);
PrintAndLogEx(SUCCESS, "Override Deadbolt: %s", override_deadbolt ? "Yes" : "No");
PrintAndLogEx(SUCCESS, "Restricted Weekdays: %s", restricted_weekday_string);
PrintAndLogEx(SUCCESS, "Property ID: %u (0x%04X)", property_id, property_id);
PrintAndLogEx(SUCCESS, "Creation Date: %04u-%02u-%02u %02u:%02u", creation_year, creation_month, creation_day, creation_hour, creation_minute);
PrintAndLogEx(SUCCESS, "Expiration Date: %04u-%02u-%02u %02u:%02u", expire_year, expire_month, expire_day, expiry_hour, expiry_minute);
PrintAndLogEx(SUCCESS, "Checksum Valid: %s", checksum_valid ? "Yes" : "No");
}
static int CmdHelp(const char *Cmd);
/*
@@ -9872,7 +10167,8 @@ static int CmdHF14AMfInfo(const char *Cmd) {
&& card.sak == 0x08 && memcmp(blockdata + 5, "\x88\x04\x00\x45", 4) == 0) {
PrintAndLogEx(SUCCESS, "NXP MF1ICS5004");
} else if (fKeyType == MF_KEY_BD) {
PrintAndLogEx(SUCCESS, _RED_("Unknown card with backdoor, please report details!"));
PrintAndLogEx(SUCCESS, _RED_("Unknown card with backdoor"));
PrintAndLogEx(INFO, "Please report details!");
} else
// other cards
if (card.sak == 0x08 && memcmp(blockdata + 5, "\x88\x04\x00\x46", 4) == 0) {
@@ -9886,11 +10182,12 @@ static int CmdHF14AMfInfo(const char *Cmd) {
} else if (card.sak == 0x08 && memcmp(blockdata + 5, "\x88\x04\x00\xc0", 4) == 0) {
PrintAndLogEx(SUCCESS, "NXP MF1ICS5035");
} else {
PrintAndLogEx(SUCCESS, "unknown");
PrintAndLogEx(SUCCESS, "n/a");
}
if (e_sector[1].foundKey[MF_KEY_A] && (e_sector[1].Key[MF_KEY_A] == 0x2A2C13CC242A)) {
if (keycnt > 1 && e_sector != NULL && e_sector[1].foundKey[MF_KEY_A] && (e_sector[1].Key[MF_KEY_A] == 0x2A2C13CC242A)) {
PrintAndLogEx(SUCCESS, "dormakaba Saflok detected");
ParseAndPrintSaflokData(&e_sector[0], &e_sector[1]);
}
} else {
@@ -10229,6 +10526,64 @@ static int CmdHF14AMfISEN(const char *Cmd) {
return PM3_SUCCESS;
}
static int CmdHF14AMfBambuKeys(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf mf bambukeys",
"Generate keys for a Bambu Lab filament tag",
"hf mf bambukeys -r\n"
"hf mf bambukeys -r -d\n"
"hf mf bambukeys -u 11223344\n"
);
void *argtable[] = {
arg_param_begin,
arg_str0("u", "uid", "<hex>", "UID (4 hex bytes)"),
arg_lit0("r", NULL, "Read UID from tag"),
arg_lit0("d", NULL, "Dump keys to file"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);
int u_len = 0;
uint8_t uid[7] = {0x00};
CLIGetHexWithReturn(ctx, 1, uid, &u_len);
bool use_tag = arg_get_lit(ctx, 2);
bool dump_keys = arg_get_lit(ctx, 3);
CLIParserFree(ctx);
if (use_tag) {
// read uid from tag
int res = mf_read_uid(uid, &u_len, NULL);
if (res != PM3_SUCCESS) {
return res;
}
}
if (u_len != 4) {
PrintAndLogEx(WARNING, "Key must be 4 hex bytes");
return PM3_EINVARG;
}
PrintAndLogEx(INFO, "-----------------------------------");
PrintAndLogEx(INFO, " UID 4b... " _YELLOW_("%s"), sprint_hex(uid, 4));
PrintAndLogEx(INFO, "-----------------------------------");
uint8_t keys[32 * 6];
mfc_algo_bambu_all(uid, (void *)keys);
for (int block = 0; block < 32; block++) {
PrintAndLogEx(INFO, "%d: %012" PRIX64, block, bytes_to_num(keys + (block * 6), 6));
}
if (dump_keys) {
char fn[FILE_PATH_SIZE] = {0};
snprintf(fn, sizeof(fn), "hf-mf-%s-key", sprint_hex_inrow(uid, 4));
saveFileEx(fn, ".bin", keys, 32 * 6, spDump);
}
return PM3_SUCCESS;
}
static command_t CommandTable[] = {
{"help", CmdHelp, AlwaysAvailable, "This help"},
{"list", CmdHF14AMfList, AlwaysAvailable, "List MIFARE history"},
@@ -10247,6 +10602,7 @@ static command_t CommandTable[] = {
{"fchk", CmdHF14AMfChk_fast, IfPm3Iso14443a, "Check keys fast, targets all keys on card"},
{"decrypt", CmdHf14AMfDecryptBytes, AlwaysAvailable, "Decrypt Crypto1 data from sniff or trace"},
{"supercard", CmdHf14AMfSuperCard, IfPm3Iso14443a, "Extract info from a `super card`"},
{"bambukeys", CmdHF14AMfBambuKeys, AlwaysAvailable, "Generate key table for Bambu Lab filament tag"},
{"-----------", CmdHelp, IfPm3Iso14443a, "----------------------- " _CYAN_("operations") " -----------------------"},
{"auth4", CmdHF14AMfAuth4, IfPm3Iso14443a, "ISO14443-4 AES authentication"},
{"acl", CmdHF14AMfAcl, AlwaysAvailable, "Decode and print MIFARE Classic access rights bytes"},
+4 -4
View File
@@ -299,7 +299,7 @@ static const char *getUlev1CardSizeStr(uint8_t fsize) {
// is LSB set?
if (fsize & 1)
snprintf(buf, sizeof(buf), "%02X, (%u <-> %u bytes)", fsize, usize, lsize);
snprintf(buf, sizeof(buf), "%02X, (%u - %u bytes)", fsize, usize, lsize);
else
snprintf(buf, sizeof(buf), "%02X, (%u bytes)", fsize, lsize);
return buf;
@@ -3925,17 +3925,17 @@ static int CmdHF14AMfUAESAuth(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf mfu aesauth",
"Tests AES key on Mifare Ultralight AES tags.\n"
"If key is not specified, null key will be tried.\n"
"If no key is specified, null key will be tried.\n"
"Key index 0: DataProtKey (default)\n"
"Key index 1: UIDRetrKey\n"
"Key index 2: OriginalityKey\n",
"hf mfu aesauth\n"
"hf mfu aesauth --key <32 hex chars> --index <0..2>"
"hf mfu aesauth --key <16 hex bytes> --index <0..2>"
);
void *argtable[] = {
arg_param_begin,
arg_str0(NULL, "key", "<hex>", "Authentication key (16 bytes in hex)"),
arg_str0(NULL, "key", "<hex>", "AES key (16 hex bytes)"),
arg_int0("i", "index", "<0..2>", "Key index, default: 0"),
arg_lit0("k", NULL, "Keep field on (only if a key is provided)"),
arg_param_end
+1
View File
@@ -356,6 +356,7 @@ const static vocabulary_t vocabulary[] = {
{ 0, "hf mf fchk" },
{ 1, "hf mf decrypt" },
{ 0, "hf mf supercard" },
{ 1, "hf mf bambukeys" },
{ 0, "hf mf auth4" },
{ 1, "hf mf acl" },
{ 0, "hf mf dump" },
+14
View File
@@ -1409,6 +1409,20 @@ void str_inverse_bin(char *buf, size_t len) {
}
}
void str_trim(char *s) {
if (s == NULL) {
return;
}
// handle empty string
if (!*s) {
return;
}
char *ptr;
for (ptr = s + strlen(s) - 1; (ptr >= s) && isspace(*ptr); --ptr);
ptr[1] = '\0';
}
/**
* Converts a hex string to component "hi2", "hi" and "lo" 32-bit integers
+2
View File
@@ -168,10 +168,12 @@ void str_creplace(char *buf, size_t len, char from, char to);
void str_reverse(char *buf, size_t len);
void str_inverse_hex(char *buf, size_t len);
void str_inverse_bin(char *buf, size_t len);
void str_trim(char *s);
char *str_dup(const char *src);
char *str_ndup(const char *src, size_t len);
size_t str_nlen(const char *src, size_t maxlen);
int hexstring_to_u96(uint32_t *hi2, uint32_t *hi, uint32_t *lo, const char *str);
int binstring_to_u96(uint32_t *hi2, uint32_t *hi, uint32_t *lo, const char *str);
int binarray_to_u96(uint32_t *hi2, uint32_t *hi, uint32_t *lo, const uint8_t *arr, int arrlen);

Some files were not shown because too many files have changed in this diff Show More