mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2026-05-12 11:18:11 -07:00
Merge branch 'master' into master
Signed-off-by: Iceman <iceman@iuse.se>
This commit is contained in:
@@ -3,8 +3,18 @@ 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]
|
||||
- Extended area for Mifare keys in SPI flash to hold 4095 keys (@piotrva)
|
||||
- Fixed DESFire D40 secure channel crypto (@nvx)
|
||||
- Fixed `hf mfp info` fix signature check on 4b UID cards (@doegox)
|
||||
- Automatically set maximum read/write block when using predefined types in `hf_mf_ultimatecard` script (@piotrva)
|
||||
- Changed SPI flash detection to calculate the size instead of table lookup, updated spi_flash_decode.py script with more ICs (@ANTodorov)
|
||||
- Fixed `hf/lf tune` segfault when called from script (@doegox)
|
||||
- Added option to set and get maximum read/write block number using `hf_mf_ultimatecard` script (@piotrva)
|
||||
- Added JEDEC information for SPI flash W25Q64JV (@ANTodorov)
|
||||
- Added special iclass legacy config cards in `hf iclass configcard` (@antiklesys)
|
||||
- Added simulation function to `hf iclass legrec` (@antiklesys)
|
||||
- Added keys from Momentum firmware projects. (@onovy)
|
||||
- Added Dutch Statistics Agency default key (@eagle00789)
|
||||
|
||||
## [Orca.4.19552][2024-11-22]
|
||||
- Fixed `hf_legic.lua` - removed bit32 commands from the script (@diorch1968)
|
||||
|
||||
+1
-1
@@ -1778,7 +1778,7 @@ static void PacketReceived(PacketCommandNG *packet) {
|
||||
break;
|
||||
}
|
||||
case CMD_HF_MIFARE_ACQ_STATIC_ENCRYPTED_NONCES: {
|
||||
MifareAcquireStaticEncryptedNonces(packet->oldarg[0], packet->data.asBytes, true);
|
||||
MifareAcquireStaticEncryptedNonces(packet->oldarg[0], packet->data.asBytes, true, packet->oldarg[1], packet->oldarg[2]);
|
||||
break;
|
||||
}
|
||||
case CMD_HF_MIFARE_ACQ_NONCES: {
|
||||
|
||||
+217
-101
@@ -1036,7 +1036,7 @@ void MifareAcquireEncryptedNonces(uint32_t arg0, uint32_t arg1, uint32_t flags,
|
||||
// acquire static encrypted nonces in order to perform the attack described in
|
||||
// Philippe Teuwen, "MIFARE Classic: exposing the static encrypted nonce variant"
|
||||
//-----------------------------------------------------------------------------
|
||||
int MifareAcquireStaticEncryptedNonces(uint32_t flags, const uint8_t *key, bool reply) {
|
||||
int MifareAcquireStaticEncryptedNonces(uint32_t flags, const uint8_t *key, bool reply, uint8_t first_block_no, uint8_t first_key_type) {
|
||||
struct Crypto1State mpcs = {0, 0};
|
||||
struct Crypto1State *pcs;
|
||||
pcs = &mpcs;
|
||||
@@ -1055,6 +1055,10 @@ int MifareAcquireStaticEncryptedNonces(uint32_t flags, const uint8_t *key, bool
|
||||
uint8_t buf[MIFARE_BLOCK_SIZE] = {0x00};
|
||||
uint64_t ui64Key = bytes_to_num(key, 6);
|
||||
bool with_data = flags & 1;
|
||||
bool without_backdoor = (flags >> 1) & 1;
|
||||
if (with_data && without_backdoor) {
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
uint32_t cuid = 0;
|
||||
int16_t isOK = PM3_SUCCESS;
|
||||
uint8_t cascade_levels = 0;
|
||||
@@ -1072,121 +1076,230 @@ int MifareAcquireStaticEncryptedNonces(uint32_t flags, const uint8_t *key, bool
|
||||
|
||||
LED_C_ON();
|
||||
|
||||
for (uint16_t sec = 0; sec < MIFARE_1K_MAXSECTOR + 1; sec++) {
|
||||
uint16_t sec_gap = sec;
|
||||
if (sec >= MIFARE_1K_MAXSECTOR) {
|
||||
// gap between user blocks and advanced verification method blocks
|
||||
sec_gap += 16;
|
||||
if (without_backdoor) {
|
||||
uint32_t nt1 = 0;
|
||||
|
||||
iso14a_card_select_t card_info;
|
||||
if (iso14443a_select_card(uid, &card_info, &cuid, true, 0, true) == 0) {
|
||||
if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Can't select card (ALL)");
|
||||
isOK = PM3_ERFTRANS;
|
||||
goto out;
|
||||
}
|
||||
uint16_t blockNo = sec_gap * 4;
|
||||
for (uint8_t keyType = 0; keyType < 2; keyType++) {
|
||||
// Test if the action was cancelled
|
||||
if (BUTTON_PRESS()) {
|
||||
isOK = PM3_EOPABORTED;
|
||||
switch (card_info.uidlen) {
|
||||
case 4 :
|
||||
cascade_levels = 1;
|
||||
break;
|
||||
case 7 :
|
||||
cascade_levels = 2;
|
||||
break;
|
||||
case 10:
|
||||
cascade_levels = 3;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (mifare_classic_authex_cmd(pcs, cuid, first_block_no, MIFARE_AUTH_KEYA + first_key_type, ui64Key, AUTH_FIRST, &nt1, NULL, NULL, NULL, false, false)) {
|
||||
if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Auth1 error");
|
||||
isOK = PM3_ESOFT;
|
||||
goto out;
|
||||
};
|
||||
|
||||
uint16_t len = mifare_sendcmd_short(pcs, AUTH_NESTED, MIFARE_AUTH_KEYA + first_key_type, first_block_no, receivedAnswer, sizeof(receivedAnswer), par_enc, NULL);
|
||||
if (len != 4) {
|
||||
if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Auth2 error len=%d", len);
|
||||
isOK = PM3_ESOFT;
|
||||
goto out;
|
||||
}
|
||||
uint32_t nt_enc = bytes_to_num(receivedAnswer, 4);
|
||||
|
||||
// send some crap to fail auth
|
||||
CHK_TIMEOUT();
|
||||
|
||||
if (iso14443a_fast_select_card(uid, cascade_levels) == 0) {
|
||||
if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Can't select card (UID)");
|
||||
isOK = PM3_ERFTRANS;
|
||||
goto out;
|
||||
}
|
||||
if (mifare_classic_authex_cmd(pcs, cuid, first_block_no, MIFARE_AUTH_KEYA + first_key_type, ui64Key, AUTH_FIRST, &nt1, NULL, NULL, NULL, false, false)) {
|
||||
if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Auth1 error");
|
||||
isOK = PM3_ESOFT;
|
||||
goto out;
|
||||
};
|
||||
// Recover clear nt
|
||||
struct Crypto1State mpcs_tmp = {0, 0};
|
||||
struct Crypto1State *pcs_tmp = &mpcs_tmp;
|
||||
crypto1_init(pcs_tmp, ui64Key);
|
||||
uint32_t nt = crypto1_word(pcs_tmp, nt_enc ^ cuid, 1) ^ nt_enc;
|
||||
int dist = nonce_distance(nt, nt1);
|
||||
// ref dist is not always stable. Adjust physical distance to maximise ref dist, and try values around estimated nonces...
|
||||
Dbprintf("Block %2i key %i nested nT=%08x first nT=%08x dist=%i", first_block_no, first_key_type, nt, nt1, dist);
|
||||
|
||||
for (uint16_t sec = 0; sec < MIFARE_1K_MAXSECTOR + 1; sec++) {
|
||||
uint16_t sec_gap = sec;
|
||||
if (sec >= MIFARE_1K_MAXSECTOR) {
|
||||
// gap between user blocks and advanced verification method blocks
|
||||
sec_gap += 16;
|
||||
}
|
||||
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) == 0) {
|
||||
if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Can't select card (ALL)");
|
||||
isOK = PM3_ERFTRANS;
|
||||
uint16_t blockNo = sec_gap * 4;
|
||||
for (uint8_t keyType = 0; keyType < 2; keyType++) {
|
||||
// Test if the action was cancelled
|
||||
if (BUTTON_PRESS()) {
|
||||
isOK = PM3_EOPABORTED;
|
||||
break;
|
||||
}
|
||||
|
||||
len = mifare_sendcmd_short(pcs, AUTH_NESTED, MIFARE_AUTH_KEYA + keyType, blockNo, receivedAnswer, sizeof(receivedAnswer), par_enc, NULL);
|
||||
if (len != 4) {
|
||||
if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Auth2 error len=%d", len);
|
||||
isOK = PM3_ESOFT;
|
||||
goto out;
|
||||
}
|
||||
switch (card_info.uidlen) {
|
||||
case 4 :
|
||||
cascade_levels = 1;
|
||||
break;
|
||||
case 7 :
|
||||
cascade_levels = 2;
|
||||
break;
|
||||
case 10:
|
||||
cascade_levels = 3;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
have_uid = true;
|
||||
} else { // no need for anticollision. We can directly select the card
|
||||
// store nt_enc
|
||||
memcpy(buf + (keyType * 8) + 4, receivedAnswer, 4);
|
||||
nt_enc = bytes_to_num(receivedAnswer, 4);
|
||||
uint8_t nt_par_err = ((((par_enc[0] >> 7) & 1) ^ oddparity8((nt_enc >> 24) & 0xFF)) << 3 |
|
||||
(((par_enc[0] >> 6) & 1) ^ oddparity8((nt_enc >> 16) & 0xFF)) << 2 |
|
||||
(((par_enc[0] >> 5) & 1) ^ oddparity8((nt_enc >> 8) & 0xFF)) << 1 |
|
||||
(((par_enc[0] >> 4) & 1) ^ oddparity8((nt_enc >> 0) & 0xFF)));
|
||||
// Dbprintf("Sec %2i key %i {nT}=%02x%02x%02x%02x perr=%x", sec, keyType, receivedAnswer[0], receivedAnswer[1], receivedAnswer[2], receivedAnswer[3], nt_par_err);
|
||||
// store nt_par_err
|
||||
buf[(keyType * 8) + 2] = nt_par_err;
|
||||
buf[(keyType * 8) + 3] = 0xAA; // extra check to tell we have nt/nt_enc/par_err
|
||||
|
||||
// send some crap to fail auth
|
||||
CHK_TIMEOUT();
|
||||
|
||||
if (iso14443a_fast_select_card(uid, cascade_levels) == 0) {
|
||||
if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Can't select card (UID)");
|
||||
isOK = PM3_ERFTRANS;
|
||||
goto out;
|
||||
}
|
||||
if (mifare_classic_authex_cmd(pcs, cuid, first_block_no, MIFARE_AUTH_KEYA + first_key_type, ui64Key, AUTH_FIRST, &nt1, NULL, NULL, NULL, false, false)) {
|
||||
if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Auth1 error");
|
||||
isOK = PM3_ESOFT;
|
||||
goto out;
|
||||
};
|
||||
nt1 = rewind_nonce(nt1, dist);
|
||||
num_to_bytes(nt1 >> 16, 2, buf + (keyType * 8));
|
||||
emlSetMem_xt(buf, (CARD_MEMORY_RF08S_OFFSET / MIFARE_BLOCK_SIZE) + sec, 1, MIFARE_BLOCK_SIZE);
|
||||
}
|
||||
|
||||
uint32_t nt1 = 0;
|
||||
if (mifare_classic_authex_cmd(pcs, cuid, blockNo, MIFARE_AUTH_KEYA + keyType + 4, ui64Key, AUTH_FIRST, &nt1, NULL, NULL, NULL, false, false)) {
|
||||
if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Auth1 error");
|
||||
isOK = PM3_ESOFT;
|
||||
goto out;
|
||||
};
|
||||
if ((with_data) && (keyType == 0)) {
|
||||
uint8_t data[16];
|
||||
uint8_t blocks = 4;
|
||||
if (blockNo >= MIFARE_1K_MAXSECTOR * 4) {
|
||||
// special RF08S advanced authentication blocks, let's dump in emulator just in case
|
||||
blocks = 8;
|
||||
}
|
||||
} else {
|
||||
for (uint16_t sec = 0; sec < MIFARE_1K_MAXSECTOR + 1; sec++) {
|
||||
uint16_t sec_gap = sec;
|
||||
if (sec >= MIFARE_1K_MAXSECTOR) {
|
||||
// gap between user blocks and advanced verification method blocks
|
||||
sec_gap += 16;
|
||||
}
|
||||
uint16_t blockNo = sec_gap * 4;
|
||||
for (uint8_t keyType = 0; keyType < 2; keyType++) {
|
||||
// Test if the action was cancelled
|
||||
if (BUTTON_PRESS()) {
|
||||
isOK = PM3_EOPABORTED;
|
||||
break;
|
||||
}
|
||||
for (uint16_t tb = blockNo; tb < blockNo + blocks; tb++) {
|
||||
memset(data, 0x00, sizeof(data));
|
||||
int res = mifare_classic_readblock(pcs, tb, data);
|
||||
if (res == 1) {
|
||||
if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Read error");
|
||||
isOK = PM3_ESOFT;
|
||||
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) == 0) {
|
||||
if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Can't select card (ALL)");
|
||||
isOK = PM3_ERFTRANS;
|
||||
goto out;
|
||||
}
|
||||
switch (card_info.uidlen) {
|
||||
case 4 :
|
||||
cascade_levels = 1;
|
||||
break;
|
||||
case 7 :
|
||||
cascade_levels = 2;
|
||||
break;
|
||||
case 10:
|
||||
cascade_levels = 3;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
have_uid = true;
|
||||
} else { // no need for anticollision. We can directly select the card
|
||||
if (iso14443a_fast_select_card(uid, cascade_levels) == 0) {
|
||||
if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Can't select card (UID)");
|
||||
isOK = PM3_ERFTRANS;
|
||||
goto out;
|
||||
}
|
||||
emlSetMem_xt(data, tb, 1, 16);
|
||||
}
|
||||
}
|
||||
// nested authentication
|
||||
uint16_t len = mifare_sendcmd_short(pcs, AUTH_NESTED, MIFARE_AUTH_KEYA + keyType + 4, blockNo, receivedAnswer, sizeof(receivedAnswer), par_enc, NULL);
|
||||
if (len != 4) {
|
||||
if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Auth2 error len=%d", len);
|
||||
isOK = PM3_ESOFT;
|
||||
goto out;
|
||||
}
|
||||
uint32_t nt_enc = bytes_to_num(receivedAnswer, 4);
|
||||
crypto1_init(pcs, ui64Key);
|
||||
uint32_t nt = crypto1_word(pcs, nt_enc ^ cuid, 1) ^ nt_enc;
|
||||
// Dbprintf("Sec %2i key %i nT=%08x", sec, keyType + 4, nt);
|
||||
// store nt (first half)
|
||||
num_to_bytes(nt >> 16, 2, buf + (keyType * 8));
|
||||
// send some crap to fail auth
|
||||
uint8_t nack[] = {0x04};
|
||||
ReaderTransmit(nack, sizeof(nack), NULL);
|
||||
|
||||
if (iso14443a_fast_select_card(uid, cascade_levels) == 0) {
|
||||
if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Can't select card (UID)");
|
||||
isOK = PM3_ERFTRANS;
|
||||
goto out;
|
||||
}
|
||||
if (mifare_classic_authex_cmd(pcs, cuid, blockNo, MIFARE_AUTH_KEYA + keyType + 4, ui64Key, AUTH_FIRST, &nt1, NULL, NULL, NULL, false, false)) {
|
||||
if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Auth1 error");
|
||||
isOK = PM3_ESOFT;
|
||||
goto out;
|
||||
};
|
||||
uint32_t nt1 = 0;
|
||||
if (mifare_classic_authex_cmd(pcs, cuid, blockNo, MIFARE_AUTH_KEYA + keyType + 4, ui64Key, AUTH_FIRST, &nt1, NULL, NULL, NULL, false, false)) {
|
||||
if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Auth1 error");
|
||||
isOK = PM3_ESOFT;
|
||||
goto out;
|
||||
};
|
||||
if ((with_data) && (keyType == 0)) {
|
||||
uint8_t data[16];
|
||||
uint8_t blocks = 4;
|
||||
if (blockNo >= MIFARE_1K_MAXSECTOR * 4) {
|
||||
// special RF08S advanced authentication blocks, let's dump in emulator just in case
|
||||
blocks = 8;
|
||||
}
|
||||
for (uint16_t tb = blockNo; tb < blockNo + blocks; tb++) {
|
||||
memset(data, 0x00, sizeof(data));
|
||||
int res = mifare_classic_readblock(pcs, tb, data);
|
||||
if (res == 1) {
|
||||
if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Read error");
|
||||
isOK = PM3_ESOFT;
|
||||
goto out;
|
||||
}
|
||||
emlSetMem_xt(data, tb, 1, 16);
|
||||
}
|
||||
}
|
||||
// nested authentication
|
||||
uint16_t len = mifare_sendcmd_short(pcs, AUTH_NESTED, MIFARE_AUTH_KEYA + keyType + 4, blockNo, receivedAnswer, sizeof(receivedAnswer), par_enc, NULL);
|
||||
if (len != 4) {
|
||||
if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Auth2 error len=%d", len);
|
||||
isOK = PM3_ESOFT;
|
||||
goto out;
|
||||
}
|
||||
uint32_t nt_enc = bytes_to_num(receivedAnswer, 4);
|
||||
crypto1_init(pcs, ui64Key);
|
||||
uint32_t nt = crypto1_word(pcs, nt_enc ^ cuid, 1) ^ nt_enc;
|
||||
// Dbprintf("Sec %2i key %i nT=%08x", sec, keyType + 4, nt);
|
||||
// store nt (first half)
|
||||
num_to_bytes(nt >> 16, 2, buf + (keyType * 8));
|
||||
// send some crap to fail auth
|
||||
CHK_TIMEOUT();
|
||||
|
||||
// nested authentication on regular keytype
|
||||
len = mifare_sendcmd_short(pcs, AUTH_NESTED, MIFARE_AUTH_KEYA + keyType, blockNo, receivedAnswer, sizeof(receivedAnswer), par_enc, NULL);
|
||||
if (len != 4) {
|
||||
if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Auth2 error len=%d", len);
|
||||
isOK = PM3_ESOFT;
|
||||
goto out;
|
||||
if (iso14443a_fast_select_card(uid, cascade_levels) == 0) {
|
||||
if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Can't select card (UID)");
|
||||
isOK = PM3_ERFTRANS;
|
||||
goto out;
|
||||
}
|
||||
if (mifare_classic_authex_cmd(pcs, cuid, blockNo, MIFARE_AUTH_KEYA + keyType + 4, ui64Key, AUTH_FIRST, &nt1, NULL, NULL, NULL, false, false)) {
|
||||
if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Auth1 error");
|
||||
isOK = PM3_ESOFT;
|
||||
goto out;
|
||||
};
|
||||
|
||||
// nested authentication on regular keytype
|
||||
len = mifare_sendcmd_short(pcs, AUTH_NESTED, MIFARE_AUTH_KEYA + keyType, blockNo, receivedAnswer, sizeof(receivedAnswer), par_enc, NULL);
|
||||
if (len != 4) {
|
||||
if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Auth2 error len=%d", len);
|
||||
isOK = PM3_ESOFT;
|
||||
goto out;
|
||||
}
|
||||
// store nt_enc
|
||||
memcpy(buf + (keyType * 8) + 4, receivedAnswer, 4);
|
||||
nt_enc = bytes_to_num(receivedAnswer, 4);
|
||||
uint8_t nt_par_err = ((((par_enc[0] >> 7) & 1) ^ oddparity8((nt_enc >> 24) & 0xFF)) << 3 |
|
||||
(((par_enc[0] >> 6) & 1) ^ oddparity8((nt_enc >> 16) & 0xFF)) << 2 |
|
||||
(((par_enc[0] >> 5) & 1) ^ oddparity8((nt_enc >> 8) & 0xFF)) << 1 |
|
||||
(((par_enc[0] >> 4) & 1) ^ oddparity8((nt_enc >> 0) & 0xFF)));
|
||||
// Dbprintf("Sec %2i key %i {nT}=%02x%02x%02x%02x perr=%x", sec, keyType, receivedAnswer[0], receivedAnswer[1], receivedAnswer[2], receivedAnswer[3], nt_par_err);
|
||||
// store nt_par_err
|
||||
buf[(keyType * 8) + 2] = nt_par_err;
|
||||
buf[(keyType * 8) + 3] = 0xAA; // extra check to tell we have nt/nt_enc/par_err
|
||||
emlSetMem_xt(buf, (CARD_MEMORY_RF08S_OFFSET / MIFARE_BLOCK_SIZE) + sec, 1, MIFARE_BLOCK_SIZE);
|
||||
// send some crap to fail auth
|
||||
CHK_TIMEOUT();
|
||||
}
|
||||
// store nt_enc
|
||||
memcpy(buf + (keyType * 8) + 4, receivedAnswer, 4);
|
||||
nt_enc = bytes_to_num(receivedAnswer, 4);
|
||||
uint8_t nt_par_err = ((((par_enc[0] >> 7) & 1) ^ oddparity8((nt_enc >> 24) & 0xFF)) << 3 |
|
||||
(((par_enc[0] >> 6) & 1) ^ oddparity8((nt_enc >> 16) & 0xFF)) << 2 |
|
||||
(((par_enc[0] >> 5) & 1) ^ oddparity8((nt_enc >> 8) & 0xFF)) << 1 |
|
||||
(((par_enc[0] >> 4) & 1) ^ oddparity8((nt_enc >> 0) & 0xFF)));
|
||||
// Dbprintf("Sec %2i key %i {nT}=%02x%02x%02x%02x perr=%x", sec, keyType, receivedAnswer[0], receivedAnswer[1], receivedAnswer[2], receivedAnswer[3], nt_par_err);
|
||||
// store nt_par_err
|
||||
buf[(keyType * 8) + 2] = nt_par_err;
|
||||
buf[(keyType * 8) + 3] = 0xAA; // extra check to tell we have nt/nt_enc/par_err
|
||||
emlSetMem_xt(buf, (CARD_MEMORY_RF08S_OFFSET / MIFARE_BLOCK_SIZE) + sec, 1, MIFARE_BLOCK_SIZE);
|
||||
// send some crap to fail auth
|
||||
ReaderTransmit(nack, sizeof(nack), NULL);
|
||||
}
|
||||
}
|
||||
out:
|
||||
@@ -1194,7 +1307,7 @@ out:
|
||||
crypto1_deinit(pcs);
|
||||
LED_B_ON();
|
||||
if (reply) {
|
||||
reply_old(CMD_ACK, isOK, cuid, 0, BigBuf_get_EM_addr() + CARD_MEMORY_RF08S_OFFSET, MIFARE_BLOCK_SIZE * (MIFARE_1K_MAXSECTOR + 1));
|
||||
reply_mix(CMD_ACK, isOK, cuid, 0, BigBuf_get_EM_addr() + CARD_MEMORY_RF08S_OFFSET, MIFARE_BLOCK_SIZE * (MIFARE_1K_MAXSECTOR + 1));
|
||||
}
|
||||
LED_B_OFF();
|
||||
|
||||
@@ -3127,7 +3240,8 @@ void MifareHasStaticEncryptedNonce(uint8_t block_no, uint8_t key_type, uint8_t *
|
||||
goto OUT;
|
||||
};
|
||||
first_nt_counter++;
|
||||
} else for (uint8_t i = 0; i < nr_nested; i++) {
|
||||
} else {
|
||||
for (uint8_t i = 0; i < nr_nested; i++) {
|
||||
if (need_first_auth) {
|
||||
cuid = 0;
|
||||
|
||||
@@ -3204,6 +3318,7 @@ void MifareHasStaticEncryptedNonce(uint8_t block_no, uint8_t key_type, uint8_t *
|
||||
}
|
||||
oldntenc = ntenc;
|
||||
}
|
||||
}
|
||||
|
||||
data[1] = (cuid >> 24) & 0xFF;
|
||||
data[2] = (cuid >> 16) & 0xFF;
|
||||
@@ -3367,7 +3482,8 @@ void MifareGen3Blk(uint8_t block_len, uint8_t *block) {
|
||||
retval = PM3_ESOFT;
|
||||
goto OUT;
|
||||
}
|
||||
cmd[ofs++] = card_info->sak;
|
||||
cmd[ofs] = block_len <= card_info->uidlen ? card_info->sak : cmd[ofs];
|
||||
ofs++;
|
||||
cmd[ofs++] = card_info->atqa[0];
|
||||
cmd[ofs++] = card_info->atqa[1];
|
||||
AddCrc14A(cmd, sizeof(block_cmd) + MIFARE_BLOCK_SIZE);
|
||||
|
||||
+1
-1
@@ -37,7 +37,7 @@ void MifareNested(uint8_t blockNo, uint8_t keyType, uint8_t targetBlockNo, uint8
|
||||
void MifareStaticNested(uint8_t blockNo, uint8_t keyType, uint8_t targetBlockNo, uint8_t targetKeyType, uint8_t *key);
|
||||
|
||||
void MifareAcquireEncryptedNonces(uint32_t arg0, uint32_t arg1, uint32_t flags, uint8_t *datain);
|
||||
int MifareAcquireStaticEncryptedNonces(uint32_t flags, const uint8_t *key, bool reply);
|
||||
int MifareAcquireStaticEncryptedNonces(uint32_t flags, const uint8_t *key, bool reply, uint8_t first_block_no, uint8_t first_key_type);
|
||||
void MifareAcquireNonces(uint32_t arg0, uint32_t flags);
|
||||
void MifareChkKeys(uint8_t *datain, uint8_t reserved_mem);
|
||||
void MifareChkKeys_fast(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain);
|
||||
|
||||
@@ -983,3 +983,12 @@ int nonce_distance(uint32_t from, uint32_t to) {
|
||||
int nonce16_index(uint16_t nt) {
|
||||
return nonce16_distance(0x0100, nt) + 1;
|
||||
}
|
||||
|
||||
uint32_t rewind_nonce(uint32_t from, uint16_t dist) {
|
||||
uint16_t x = from >> 16;
|
||||
for (uint16_t i = 0; i < dist; i++) {
|
||||
x = ((x << 1 | x >> 15) & 0xffff) ^ ((x >> 1 ^ x >> 2 ^ x >> 4) & 0x100);
|
||||
}
|
||||
uint32_t nt = x;
|
||||
return nt << 16 | prng_successor(nt, 16);
|
||||
}
|
||||
|
||||
@@ -128,4 +128,5 @@ bool validate_parity_nonce(uint32_t ntenc, uint8_t ntparenc, uint32_t nt);
|
||||
int nonce_distance(uint32_t from, uint32_t to);
|
||||
int nonce16_distance(uint16_t x, uint16_t y);
|
||||
int nonce16_index(uint16_t nt);
|
||||
uint32_t rewind_nonce(uint32_t from, uint16_t dist);
|
||||
#endif
|
||||
|
||||
@@ -2755,7 +2755,8 @@ D37C8F1793F7
|
||||
543071543071
|
||||
5F01015F0101
|
||||
200510241234
|
||||
# Momentum-Firmware
|
||||
#
|
||||
# Momentum-Firmware 20241201
|
||||
AC935925A876
|
||||
ADC169F922CB
|
||||
AD00EFD353E4
|
||||
@@ -3022,3 +3023,18 @@ F833E24C3F1C
|
||||
93FB38FE585A
|
||||
96AECCC0F7EB
|
||||
96227EDADBCF
|
||||
#
|
||||
# BW Kantine
|
||||
56cf3acd90ca
|
||||
542089792be2
|
||||
5420aeada758
|
||||
#
|
||||
# CSC Laundry
|
||||
212223242555
|
||||
717273747555
|
||||
#
|
||||
# Hotel cards, BETECH brand, Vietnam
|
||||
AAC34D9A4E65
|
||||
#
|
||||
# Dutch Statistics Agency (CBS)
|
||||
DC7B15AA0938
|
||||
@@ -22,6 +22,8 @@ F9DCEBA0
|
||||
89A69E60
|
||||
# ref lock
|
||||
314159E0
|
||||
#Zonsin ZX-COPY10
|
||||
7B3D5C48
|
||||
# ref. http://www.proxmark.org/forum/viewtopic.php?pid=28115#p28115
|
||||
AA55BBBB
|
||||
# ref. http://www.proxmark.org/forum/viewtopic.php?pid=33376#p33376
|
||||
|
||||
@@ -50,20 +50,20 @@ arguments = [[
|
||||
-c read magic configuration
|
||||
-u UID (8-20 hexsymbols), set UID on tag
|
||||
-t tag type to impersonate
|
||||
1 = Mifare Mini S20 4-byte
|
||||
2 = Mifare Mini S20 7-byte 15 = NTAG 210
|
||||
3 = Mifare Mini S20 10-byte 16 = NTAG 212
|
||||
4 = Mifare 1k S50 4-byte 17 = NTAG 213
|
||||
5 = Mifare 1k S50 7-byte 18 = NTAG 215
|
||||
6 = Mifare 1k S50 10-byte 19 = NTAG 216
|
||||
7 = Mifare 4k S70 4-byte 20 = NTAG I2C 1K
|
||||
8 = Mifare 4k S70 7-byte 21 = NTAG I2C 2K
|
||||
9 = Mifare 4k S70 10-byte 22 = NTAG I2C 1K PLUS
|
||||
*** 10 = UL - NOT WORKING FULLY 23 = NTAG I2C 2K PLUS
|
||||
*** 11 = UL-C - NOT WORKING FULLY 24 = NTAG 213F
|
||||
12 = UL EV1 48b 25 = NTAG 216F
|
||||
13 = UL EV1 128b
|
||||
*** 14 = UL Plus - NOT WORKING YET
|
||||
1 = Mifare Mini S20 4-byte | 15 = NTAG 210
|
||||
2 = Mifare Mini S20 7-byte | 16 = NTAG 212
|
||||
3 = Mifare Mini S20 10-byte | 17 = NTAG 213
|
||||
4 = Mifare 1k S50 4-byte | 18 = NTAG 215
|
||||
5 = Mifare 1k S50 7-byte | 19 = NTAG 216
|
||||
6 = Mifare 1k S50 10-byte | 20 = NTAG I2C 1K
|
||||
7 = Mifare 4k S70 4-byte | 21 = NTAG I2C 2K
|
||||
8 = Mifare 4k S70 7-byte | 22 = NTAG I2C 1K PLUS
|
||||
9 = Mifare 4k S70 10-byte | 23 = NTAG I2C 2K PLUS
|
||||
*** 10 = UL - NOT WORKING FULLY | 24 = NTAG 213F
|
||||
*** 11 = UL-C - NOT WORKING FULLY | 25 = NTAG 216F
|
||||
12 = UL EV1 48b |
|
||||
13 = UL EV1 128b |
|
||||
*** 14 = UL Plus - NOT WORKING YET |
|
||||
|
||||
-p NTAG password (8 hexsymbols), set NTAG password on tag.
|
||||
-a NTAG pack ( 4 hexsymbols), set NTAG pack on tag.
|
||||
@@ -75,7 +75,11 @@ arguments = [[
|
||||
-z ATS (<1b length><0-16 ATS> hexsymbols), Configure ATS. Length set to 00 will disable ATS.
|
||||
-w Wipe tag. 0 for Mifare or 1 for UL. Fills tag with zeros and put default values for type selected.
|
||||
-m Ultralight mode (00 UL EV1, 01 NTAG, 02 UL-C, 03 UL) Set type of UL.
|
||||
-n Ultralight protocol (00 MFC, 01 UL), switches between UL and MFC mode
|
||||
-n Ultralight protocol (00 MFC, 01 UL), switches between UL and MFC mode]]
|
||||
-- Need to split because reached maximum string length processed by lua
|
||||
arguments2 = [[
|
||||
-b Set maximum read/write blocks (2 hexsymbols)
|
||||
NOTE: Ultralight EV1 and NTAG Version info and Signature are stored respectively in blocks 250-251 and 242-249
|
||||
-k Ultimate Magic Card Key (IF DIFFERENT THAN DEFAULT 00000000)
|
||||
]]
|
||||
---
|
||||
@@ -110,6 +114,7 @@ local function help()
|
||||
print(usage)
|
||||
print(ansicolors.cyan..'Arguments'..ansicolors.reset)
|
||||
print(arguments)
|
||||
print(arguments2)
|
||||
print(ansicolors.cyan..'Example usage'..ansicolors.reset)
|
||||
print(example)
|
||||
end
|
||||
@@ -186,6 +191,7 @@ local function read_config()
|
||||
end
|
||||
-- extract data from CONFIG - based on CONFIG in https://github.com/RfidResearchGroup/proxmark3/blob/master/doc/magic_cards_notes.md#gen-4-gtu
|
||||
ulprotocol, uidlength, readpass, gtumode, ats, atqa1, atqa2, sak, ulmode = magicconfig:sub(1,2), magicconfig:sub(3,4), magicconfig:sub(5,12), magicconfig:sub(13,14), magicconfig:sub(15,48), magicconfig:sub(51,52), magicconfig:sub(49,50), magicconfig:sub(53,54), magicconfig:sub(55,56)
|
||||
maxRWblk = magicconfig:sub(57, 58)
|
||||
atqaf = atqa1..' '..atqa2
|
||||
cardtype, cardprotocol, gtustr, atsstr = 'unknown', 'unknown', 'unknown', 'unknown'
|
||||
if magicconfig == nil then lib14a.disconnect(); return nil, "can't read configuration, "..err_lock end
|
||||
@@ -291,6 +297,7 @@ local function read_config()
|
||||
print(' - Version ', cversion)
|
||||
print(' - Signature ', signature1..signature2)
|
||||
end
|
||||
print(' - Max R/W Block ', maxRWblk)
|
||||
end
|
||||
lib14a.disconnect()
|
||||
return true, 'Ok'
|
||||
@@ -637,6 +644,26 @@ local function write_ulm(ulm)
|
||||
return true, 'Ok'
|
||||
end
|
||||
---
|
||||
-- Write maximum read/write block number,
|
||||
local function write_maxRWblk(data)
|
||||
-- input number check
|
||||
if data == nil then return nil, 'empty block number' end
|
||||
if #data == 0 then return nil, 'empty block number' end
|
||||
if #data ~= 2 then return nil, 'block number wrong length. Should be 1 hex byte' end
|
||||
|
||||
print('Set max R/W block', data)
|
||||
local info = connect()
|
||||
if not info then return false, "Can't select card" end
|
||||
local resp
|
||||
-- set maximum read/write block
|
||||
resp = send("CF".._key.."6B"..data)
|
||||
lib14a.disconnect()
|
||||
if resp ~= '9000FD07' then return nil, 'Failed to write maximum read/write block'
|
||||
else
|
||||
return true, 'Ok'
|
||||
end
|
||||
end
|
||||
---
|
||||
-- Set type for magic card presets.
|
||||
local function set_type(tagtype)
|
||||
-- tagtype checks
|
||||
@@ -649,6 +676,7 @@ local function set_type(tagtype)
|
||||
send("CF".._key.."F000000000000002000978009102DABC19101011121314151604000900")
|
||||
lib14a.disconnect()
|
||||
write_uid('04112233')
|
||||
write_maxRWblk('13')
|
||||
-- Setting Mifare mini S20 7-byte
|
||||
elseif tagtype == 2 then
|
||||
print('Setting: Ultimate Magic card to Mifare mini S20 7-byte')
|
||||
@@ -656,6 +684,7 @@ local function set_type(tagtype)
|
||||
send("CF".._key.."F000010000000002000978009102DABC19101011121314151644000900")
|
||||
lib14a.disconnect()
|
||||
write_uid('04112233445566')
|
||||
write_maxRWblk('13')
|
||||
-- Setting Mifare mini S20 10-byte
|
||||
elseif tagtype == 3 then
|
||||
print('Setting: Ultimate Magic card to Mifare mini S20 10-byte')
|
||||
@@ -663,6 +692,7 @@ local function set_type(tagtype)
|
||||
send("CF".._key.."F000020000000002000978009102DABC19101011121314151684000900")
|
||||
lib14a.disconnect()
|
||||
write_uid('04112233445566778899')
|
||||
write_maxRWblk('13')
|
||||
-- Setting Mifare 1k S50 4--byte
|
||||
elseif tagtype == 4 then
|
||||
print('Setting: Ultimate Magic card to Mifare 1k S50 4-byte')
|
||||
@@ -670,6 +700,7 @@ local function set_type(tagtype)
|
||||
send("CF".._key.."F000000000000002000978009102DABC19101011121314151604000800")
|
||||
lib14a.disconnect()
|
||||
write_uid('04112233')
|
||||
write_maxRWblk('3F')
|
||||
-- Setting Mifare 1k S50 7-byte
|
||||
elseif tagtype == 5 then
|
||||
print('Setting: Ultimate Magic card to Mifare 1k S50 7-byte')
|
||||
@@ -677,6 +708,7 @@ local function set_type(tagtype)
|
||||
send("CF".._key.."F000010000000002000978009102DABC19101011121314151644000800")
|
||||
lib14a.disconnect()
|
||||
write_uid('04112233445566')
|
||||
write_maxRWblk('3F')
|
||||
-- Setting Mifare 1k S50 10-byte
|
||||
elseif tagtype == 6 then
|
||||
print('Setting: Ultimate Magic card to Mifare 1k S50 10-byte')
|
||||
@@ -684,6 +716,7 @@ local function set_type(tagtype)
|
||||
send("CF".._key.."F000020000000002000978009102DABC19101011121314151684000800")
|
||||
lib14a.disconnect()
|
||||
write_uid('04112233445566778899')
|
||||
write_maxRWblk('3F')
|
||||
-- Setting Mifare 4k S70 4-byte
|
||||
elseif tagtype == 7 then
|
||||
print('Setting: Ultimate Magic card to Mifare 4k S70 4-byte')
|
||||
@@ -691,6 +724,7 @@ local function set_type(tagtype)
|
||||
send("CF".._key.."F000000000000002000978009102DABC19101011121314151602001800")
|
||||
lib14a.disconnect()
|
||||
write_uid('04112233')
|
||||
write_maxRWblk('FF')
|
||||
-- Setting Mifare 4k S70 7-byte
|
||||
elseif tagtype == 8 then
|
||||
print('Setting: Ultimate Magic card to Mifare 4k S70 7-byte')
|
||||
@@ -698,6 +732,7 @@ local function set_type(tagtype)
|
||||
send("CF".._key.."F000010000000002000978009102DABC19101011121314151642001800")
|
||||
lib14a.disconnect()
|
||||
write_uid('04112233445566')
|
||||
write_maxRWblk('FF')
|
||||
-- Setting Mifare 4k S70 10-byte
|
||||
elseif tagtype == 9 then
|
||||
print('Setting: Ultimate Magic card to Mifare 4k S70 10-byte')
|
||||
@@ -705,6 +740,7 @@ local function set_type(tagtype)
|
||||
send("CF".._key.."F000020000000002000978009102DABC19101011121314151682001800")
|
||||
lib14a.disconnect()
|
||||
write_uid('04112233445566778899')
|
||||
write_maxRWblk('FF')
|
||||
-- Setting UL
|
||||
elseif tagtype == 10 then
|
||||
print('Setting: Ultimate Magic card to UL')
|
||||
@@ -1016,7 +1052,7 @@ function main(args)
|
||||
local err, msg
|
||||
if #args == 0 then return help() end
|
||||
-- Read the parameters
|
||||
for o, a in getopt.getopt(args, 'hck:u:t:p:a:s:o:v:q:g:z:n:m:w:') do
|
||||
for o, a in getopt.getopt(args, 'hck:u:t:p:a:s:o:v:q:g:z:n:m:w:b:') do
|
||||
-- help
|
||||
if o == "h" then return help() end
|
||||
-- set Ultimate Magic Card Key for read write
|
||||
@@ -1049,6 +1085,8 @@ function main(args)
|
||||
if o == "m" then err, msg = write_ulm(a) end
|
||||
-- write UL protocol
|
||||
if o == "n" then err, msg = write_ulp(a) end
|
||||
-- write max r/w block
|
||||
if o == "b" then err, msg = write_maxRWblk(a) end
|
||||
if err == nil then return oops(msg) end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -12,62 +12,118 @@ except ModuleNotFoundError:
|
||||
return str(s)
|
||||
|
||||
spi = {
|
||||
0x68:{
|
||||
"manufacturer": "Boya",
|
||||
"jedec" : {
|
||||
0x40: {
|
||||
0x15: {
|
||||
"part": "BY25Q16BS",
|
||||
"size": "16mbits",
|
||||
"sizeB": "2MB",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
0x85:{
|
||||
"manufacturer": "Puya",
|
||||
0x60: {
|
||||
0x15: {
|
||||
"part": "P25Q16H",
|
||||
"size": "16mbits",
|
||||
"sizeB": "2MB",
|
||||
"jedec" : {
|
||||
0x60: {
|
||||
0x15: {
|
||||
"part": "P25Q16H",
|
||||
"size": "16mbits",
|
||||
"sizeB": "2MB",
|
||||
},
|
||||
0x16: {
|
||||
"part": "P25Q32H",
|
||||
"size": "32mbits",
|
||||
"sizeB": "4MB",
|
||||
},
|
||||
0x17: {
|
||||
"part": "P25Q64H",
|
||||
"size": "64mbits",
|
||||
"sizeB": "8MB",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
0xEF:{
|
||||
"manufacturer": "Winbond",
|
||||
0x30: {
|
||||
0x11: {
|
||||
"part": "W25X10BV",
|
||||
"size": "1mbits",
|
||||
"sizeB": "128KB",
|
||||
"jedec" : {
|
||||
0x30: {
|
||||
0x11: {
|
||||
"part": "W25X10BV",
|
||||
"size": "1mbits",
|
||||
"sizeB": "128KB",
|
||||
},
|
||||
0x12: {
|
||||
"part": "W25X20BV",
|
||||
"size": "2mbits",
|
||||
"sizeB": "256KB",
|
||||
},
|
||||
0x13: {
|
||||
"part": "W25X40BV",
|
||||
"size": "4mbits",
|
||||
"sizeB": "512KB",
|
||||
},
|
||||
},
|
||||
0x12: {
|
||||
"part": "W25X20BV",
|
||||
"size": "2mbits",
|
||||
"sizeB": "256KB",
|
||||
0x40: {
|
||||
0x12: {
|
||||
"part": "W25Q20BV",
|
||||
"size": "2mbits",
|
||||
"sizeB": "256KB",
|
||||
},
|
||||
0x13: {
|
||||
"part": "W25Q40BV",
|
||||
"size": "4mbits",
|
||||
"sizeB": "512KB",
|
||||
},
|
||||
0x14: {
|
||||
"part": "W25Q80BV",
|
||||
"size": "8mbits",
|
||||
"sizeB": "1MB",
|
||||
},
|
||||
0x15: {
|
||||
"part": "W25Q16BV",
|
||||
"size": "16mbits",
|
||||
"sizeB": "2MB",
|
||||
},
|
||||
0x16: {
|
||||
"part": "W25Q32BV",
|
||||
"size": "32mbits",
|
||||
"sizeB": "4MB",
|
||||
},
|
||||
0x17: {
|
||||
"part": "W25Q64BV",
|
||||
"size": "64mbits",
|
||||
"sizeB": "8MB",
|
||||
},
|
||||
},
|
||||
0x13: {
|
||||
"part": "W25X40BV",
|
||||
"size": "4mbits",
|
||||
"sizeB": "512KB",
|
||||
},
|
||||
},
|
||||
0x40: {
|
||||
0x13: {
|
||||
"part": "W25Q40BV",
|
||||
"size": "4mbits",
|
||||
"sizeB": "512KB",
|
||||
},
|
||||
0x14: {
|
||||
"part": "W25Q80BV",
|
||||
"size": "8mbits",
|
||||
"sizeB": "1MB",
|
||||
},
|
||||
0x15: {
|
||||
"part": "W25Q16BV",
|
||||
"size": "16mbits",
|
||||
"sizeB": "2MB",
|
||||
},
|
||||
0x16: {
|
||||
"part": "W25Q32BV",
|
||||
"size": "32mbits",
|
||||
"sizeB": "4MB",
|
||||
},
|
||||
},
|
||||
0x70: {
|
||||
0x22: {
|
||||
"part": "W25Q02JV-IM",
|
||||
"size": "2mbits",
|
||||
"sizeB": "256KB",
|
||||
0x70: {
|
||||
0x14: {
|
||||
"part": "W25Q80JV",
|
||||
"size": "8mbits",
|
||||
"sizeB": "1MB",
|
||||
},
|
||||
0x15: {
|
||||
"part": "W25Q16JV",
|
||||
"size": "16mbits",
|
||||
"sizeB": "2MB",
|
||||
},
|
||||
0x16: {
|
||||
"part": "W25Q32JV",
|
||||
"size": "32mbits",
|
||||
"sizeB": "4MB",
|
||||
},
|
||||
0x17: {
|
||||
"part": "W25Q64JV",
|
||||
"size": "64mbits",
|
||||
"sizeB": "8MB",
|
||||
},
|
||||
0x22: {
|
||||
"part": "W25Q02JV-IM",
|
||||
"size": "2mbits",
|
||||
"sizeB": "256KB",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -90,16 +146,16 @@ for line in p.grabbed_output.split('\n'):
|
||||
did_h = did >> 8
|
||||
did_l = did & 0xff
|
||||
t = None
|
||||
|
||||
print(f"\n JEDEC ID....... 0x{mid:X} / 0x{did:X}")
|
||||
if mid in spi:
|
||||
|
||||
mfr = spi[mid]['manufacturer']
|
||||
|
||||
if did_h in spi[mid]:
|
||||
if did_h in spi[mid]['jedec']:
|
||||
|
||||
if did_l in spi[mid][did_h]:
|
||||
if did_l in spi[mid]['jedec'][did_h]:
|
||||
|
||||
t = spi[mid][did_h][did_l]
|
||||
t = spi[mid]['jedec'][did_h][did_l]
|
||||
print("\n Manufacturer... " + color(f"{mfr}", fg="green") +
|
||||
"\n Device......... " + color(f"{t['part']}", fg="green") +
|
||||
"\n Size........... " + color(f"{t['size']} ({t['sizeB']})", fg="yellow")
|
||||
|
||||
+156
-60
@@ -1,81 +1,177 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
### Parameters
|
||||
import os
|
||||
import subprocess
|
||||
import signal
|
||||
import numpy as np
|
||||
from pyaudio import PyAudio, paFloat32, paContinue
|
||||
|
||||
# Sound output parameters
|
||||
volume = 1.0
|
||||
sample_buf_size = 44
|
||||
sampling_freq = 44100 #Hz
|
||||
sampling_freq = 44100 # Hz
|
||||
|
||||
# Frequency generator parameters
|
||||
min_freq = 200 #Hz
|
||||
max_freq = 2000 #Hz
|
||||
min_freq = 100 # Hz
|
||||
max_freq = 6000 # Hz
|
||||
|
||||
# Proxmark3 parameters
|
||||
pm3_client="/usr/local/bin/proxmark3"
|
||||
pm3_reader_dev_file="/dev/ttyACM0"
|
||||
pm3_tune_cmd="hf tune"
|
||||
pm3_client = "pm3"
|
||||
pm3_tune_cmd = "hf tune --value"
|
||||
|
||||
frequency = 440
|
||||
buffer = []
|
||||
|
||||
|
||||
### Modules
|
||||
import numpy
|
||||
import pyaudio
|
||||
from select import select
|
||||
from subprocess import Popen, DEVNULL, PIPE
|
||||
def find_zero_crossing_index(array):
|
||||
for i in range(1, len(array)):
|
||||
if array[i-1] < 0 and array[i] >= 0:
|
||||
return i
|
||||
return None # Return None if no zero-crossing is found
|
||||
|
||||
|
||||
### Main program
|
||||
p = pyaudio.PyAudio()
|
||||
def generate_sine_wave(frequency, sample_rate, duration, frame_count):
|
||||
"""Generate a sine wave at a given frequency."""
|
||||
t = np.linspace(0, duration, int(sample_rate * duration), endpoint=False)
|
||||
wave = np.sin(2 * np.pi * frequency * t)
|
||||
return wave[:frame_count]
|
||||
|
||||
# For paFloat32 sample values must be in range [-1.0, 1.0]
|
||||
stream = p.open(format=pyaudio.paFloat32,
|
||||
channels=1,
|
||||
rate=sampling_freq,
|
||||
output=True)
|
||||
|
||||
# Initial voltage to frequency values
|
||||
min_v = 100.0
|
||||
max_v = 0.0
|
||||
v = 0
|
||||
out_freq = min_freq
|
||||
# PyAudio Callback function
|
||||
def pyaudio_callback(in_data, frame_count, time_info, status):
|
||||
# if in_data is None:
|
||||
# return (in_data, pyaudio.paContinue)
|
||||
global frequency
|
||||
global buffer
|
||||
wave = generate_sine_wave(frequency, sampling_freq, 0.01, frame_count*2)
|
||||
i = find_zero_crossing_index(buffer)
|
||||
if i is None:
|
||||
buffer = wave
|
||||
else:
|
||||
buffer = np.concatenate((buffer[:i], wave))
|
||||
data = (buffer[:frame_count] * volume).astype(np.float32).tobytes()
|
||||
buffer = buffer[frame_count:]
|
||||
return (data, paContinue)
|
||||
# pyaudio.paComplete
|
||||
|
||||
# Spawn the Proxmark3 client
|
||||
pm3_proc = Popen([pm3_client, pm3_reader_dev_file, "-c", pm3_tune_cmd], bufsize=0, env={}, stdin=DEVNULL, stdout=PIPE, stderr=DEVNULL)
|
||||
mv_recbuf = ""
|
||||
|
||||
# Read voltages from the Proxmark3, generate the sine wave, output to soundcard
|
||||
sample_buf = [0.0 for x in range(0, sample_buf_size)]
|
||||
i = 0
|
||||
sinev = 0
|
||||
while True:
|
||||
def silent_pyaudio():
|
||||
"""
|
||||
Lifted and adapted from https://stackoverflow.com/questions/67765911/
|
||||
PyAudio is noisy af every time you initialise it, which makes reading the
|
||||
log output rather difficult. The output appears to be being made by the
|
||||
C internals, so we can't even redirect the logs with Python's logging
|
||||
facility. Therefore the nuclear option was selected: swallow all stderr
|
||||
and stdout for the duration of PyAudio's use.
|
||||
"""
|
||||
|
||||
# Read Proxmark3 client's stdout and extract voltage values
|
||||
if(select([pm3_proc.stdout], [], [], 0)[0]):
|
||||
# Open a pair of null files
|
||||
null_fds = [os.open(os.devnull, os.O_RDWR) for x in range(2)]
|
||||
# Save the actual stdout (1) and stderr (2) file descriptors.
|
||||
save_fds = [os.dup(1), os.dup(2)]
|
||||
# Assign the null pointers to stdout and stderr.
|
||||
os.dup2(null_fds[0], 1)
|
||||
os.dup2(null_fds[1], 2)
|
||||
pyaudio = PyAudio()
|
||||
os.dup2(save_fds[0], 1)
|
||||
os.dup2(save_fds[1], 2)
|
||||
# Close all file descriptors
|
||||
for fd in null_fds + save_fds:
|
||||
os.close(fd)
|
||||
return pyaudio
|
||||
|
||||
b = pm3_proc.stdout.read(256).decode("ascii")
|
||||
if "Done" in b:
|
||||
break;
|
||||
for c in b:
|
||||
if c in "0123456789 mV":
|
||||
mv_recbuf += c
|
||||
else:
|
||||
mv_recbuf = ""
|
||||
if mv_recbuf[-3:] == " mV":
|
||||
v = int(mv_recbuf[:-3]) / 1000
|
||||
if v < min_v:
|
||||
min_v = v - 0.001
|
||||
if v > max_v:
|
||||
max_v = v
|
||||
|
||||
def run_pm3_cmd(callback):
|
||||
# Start the process
|
||||
process = subprocess.Popen(
|
||||
[pm3_client, '-c', pm3_tune_cmd],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
text=True,
|
||||
bufsize=1, # Line buffered
|
||||
shell=False
|
||||
)
|
||||
|
||||
# Read the output line by line as it comes
|
||||
try:
|
||||
with process.stdout as pipe:
|
||||
for line in pipe:
|
||||
# Process each line
|
||||
l = line.strip() # Strip to remove any extraneous newline characters
|
||||
callback(l)
|
||||
except Exception as e:
|
||||
print(f"An error occurred: {e}")
|
||||
finally:
|
||||
# Ensure the subprocess is properly terminated
|
||||
process.terminate()
|
||||
process.wait()
|
||||
|
||||
|
||||
def linear_to_exponential_freq(v, min_v, max_v, min_freq, max_freq):
|
||||
# First, map v to a range between 0 and 1
|
||||
if max_v != min_v:
|
||||
normalized_v = (v - min_v) / (max_v - min_v)
|
||||
else:
|
||||
normalized_v = 0.5
|
||||
normalized_v = 1 - normalized_v
|
||||
|
||||
# Calculate the ratio of the max frequency to the min frequency
|
||||
freq_ratio = max_freq / min_freq
|
||||
|
||||
# Calculate the exponential frequency using the mapped v
|
||||
freq = min_freq * (freq_ratio ** normalized_v)
|
||||
return freq
|
||||
|
||||
|
||||
class foo():
|
||||
def __init__(self):
|
||||
self.p = silent_pyaudio()
|
||||
# For paFloat32 sample values must be in range [-1.0, 1.0]
|
||||
self.stream = self.p.open(format=paFloat32,
|
||||
channels=1,
|
||||
rate=sampling_freq,
|
||||
output=True,
|
||||
stream_callback=pyaudio_callback)
|
||||
|
||||
# Initial voltage to frequency values
|
||||
self.min_v = 50000.0
|
||||
self.max_v = 0.0
|
||||
|
||||
# Setting the signal handler for SIGINT (Ctrl+C)
|
||||
signal.signal(signal.SIGINT, self.signal_handler)
|
||||
|
||||
# Start the stream
|
||||
self.stream.start_stream()
|
||||
|
||||
def __exit__(self):
|
||||
self.stream.stop_stream()
|
||||
self.stream.close()
|
||||
self.p.terminate()
|
||||
|
||||
def signal_handler(self, sig, frame):
|
||||
print("\nYou pressed Ctrl+C! Press Enter")
|
||||
self.__exit__()
|
||||
|
||||
def callback(self, line):
|
||||
if 'mV' not in line:
|
||||
return
|
||||
v = int(line.split(' ')[1])
|
||||
if v == 0:
|
||||
return
|
||||
self.min_v = min(self.min_v, v)
|
||||
self.max_v = max(self.max_v, v)
|
||||
|
||||
# Recalculate the audio frequency to generate
|
||||
out_freq = (max_freq - min_freq) * (max_v - v) / (max_v - min_v) \
|
||||
+ min_freq
|
||||
global frequency
|
||||
frequency = linear_to_exponential_freq(v, self.min_v, self.max_v, min_freq, max_freq)
|
||||
|
||||
# Generate the samples and write them to the soundcard
|
||||
sinevs = out_freq / sampling_freq * numpy.pi * 2
|
||||
sample_buf[i] = sinev
|
||||
sinev += sinevs
|
||||
sinev = sinev if sinev < numpy.pi * 2 else sinev - numpy.pi * 2
|
||||
i = (i + 1) % sample_buf_size
|
||||
if not i:
|
||||
stream.write((numpy.sin(sample_buf) * volume).
|
||||
astype(numpy.float32).tobytes())
|
||||
# frequency = max_freq - ((max_freq - min_freq) * (v - self.min_v) / (self.max_v - self.min_v) + min_freq)
|
||||
#frequency = (frequency + new_frequency)/2
|
||||
|
||||
|
||||
def main():
|
||||
f = foo()
|
||||
run_pm3_cmd(f.callback)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
@@ -1065,10 +1065,10 @@
|
||||
},
|
||||
{
|
||||
"AID": "DD00DD",
|
||||
"Vendor": "Regional Transporation District (RTD) via masabi justride",
|
||||
"Vendor": "Regional Transporation District (RTD) via Masabi Ltd",
|
||||
"Country": "US",
|
||||
"Name": "MyRide Card (DEN)",
|
||||
"Description": "DEN MyRide Card",
|
||||
"Description": "DEN MyRide Card; Masabi Justride Tap and Ride DESFire Smartcard",
|
||||
"Type": "transport"
|
||||
},
|
||||
{
|
||||
@@ -1151,6 +1151,22 @@
|
||||
"Description": "FIDs 02: Card Balance; 04: Refill History; 08: Card Information; 0E: Trip History",
|
||||
"Type": "transport"
|
||||
},
|
||||
{
|
||||
"AID": "F21201",
|
||||
"Vendor": "Green Bay Metro Transit via Genfare",
|
||||
"Country": "US",
|
||||
"Name": "Tap-N-Go Card (GRB)",
|
||||
"Description": "GRB Tap-N-Go Card",
|
||||
"Type": "transport"
|
||||
},
|
||||
{
|
||||
"AID": "F21202",
|
||||
"Vendor": "Green Bay Metro Transit via Genfare",
|
||||
"Country": "US",
|
||||
"Name": "Tap-N-Go Card (GRB)",
|
||||
"Description": "GRB Tap-N-Go Card",
|
||||
"Type": "transport"
|
||||
},
|
||||
{
|
||||
"AID": "F21360",
|
||||
"Vendor": "INIT",
|
||||
|
||||
@@ -264,7 +264,7 @@ static uint8_t card_app2_limit[] = {
|
||||
0xff,
|
||||
};
|
||||
|
||||
static iclass_config_card_item_t iclass_config_options[30] = {
|
||||
static iclass_config_card_item_t iclass_config_options[33] = {
|
||||
//Byte A8 - LED Operations
|
||||
{"(LED) - Led idle (Off) / Led read (Off)", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x18, 0xA8, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
|
||||
{"(LED) - Led idle (Red) / Led read (Off)", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x18, 0xA8, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
|
||||
@@ -300,11 +300,18 @@ static iclass_config_card_item_t iclass_config_options[30] = {
|
||||
{"(ELITE Key) - Set ELITE Key and Enable Dual key (Elite + Standard)", {0x0C, 0x00, 0x00, 0x01, 0x00, 0x00, 0xBF, 0x18, 0xBF, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}},
|
||||
{"(ELITE Key) - Set ELITE Key and ENABLE Keyrolling", {0x0C, 0x00, 0x00, 0x01, 0x00, 0x00, 0xBF, 0x18, 0xBF, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}},
|
||||
{"(ELITE Key) - Set ELITE Key and DISABLE Standard Key", {0x0C, 0x00, 0x00, 0x01, 0x00, 0x00, 0xBF, 0x18, 0xBF, 0x05, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}},
|
||||
//Erroneous / incorrect reader behaviors
|
||||
//Erroneous / incorrect reader behaviors (read below)
|
||||
//Elite Bugger:
|
||||
//Sets block 3 of card 0 presented to the reader to 0, sets block 3 of card 1 presented to the reader to the original value of card 0's block 3
|
||||
//Continues setting block 3 of presented cards to block 3 of the previous card the reader scanned
|
||||
//This renders cards unreadable and hardly recoverable unless the order of the scanned cards is known.
|
||||
{"(ELITE Bugger) - Renders cards unusable." , {0x0C, 0x00, 0x00, 0x01, 0x00, 0x00, 0xBF, 0x18, 0xBF, 0x02, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}},
|
||||
//Reset Operations
|
||||
{"(RESET) - Reset READER to defaults", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
|
||||
{"(RESET) - Reset ENROLLER to defaults", {0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF}}
|
||||
{"(RESET) - Reset ENROLLER to defaults", {0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF}},
|
||||
//Reader Master Key Operations
|
||||
{"(MASTER Key) - Change Reader Master Key to Custom Key", {0x28, 0xCB, 0x91, 0x9D, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
|
||||
{"(MASTER Key) - Restore Reader Master Key to Factory Defaults", {0x28, 0xCB, 0x91, 0x9D, 0x00, 0x00, 0x00, 0x1C, 0xE0, 0x5C, 0x91, 0xCF, 0x63, 0x34, 0x23, 0xB9}}
|
||||
};
|
||||
|
||||
static const iclass_config_card_item_t *get_config_card_item(int idx) {
|
||||
@@ -4145,6 +4152,7 @@ static int CmdHFiClassLegRecLookUp(const char *Cmd) {
|
||||
}
|
||||
if (check_values) {
|
||||
PrintAndLogEx(SUCCESS, _GREEN_("CONFIRMED VALID RAW key ") _RED_("%s"), sprint_hex(div_key, 8));
|
||||
PrintAndLogEx(INFO, "You can now run -> "_YELLOW_("hf iclass unhash -k %s")" <-to find the pre-images.", sprint_hex(div_key, 8));
|
||||
verified = true;
|
||||
} else {
|
||||
PrintAndLogEx(INFO, _YELLOW_("Raw Key Invalid"));
|
||||
@@ -4280,7 +4288,7 @@ static int CmdHFiClassLegacyRecover(const char *Cmd) {
|
||||
arg_lit0(NULL, "debug", "Re-enables tracing for debugging. Limits cycles to 1."),
|
||||
arg_lit0(NULL, "notest", "Perform real writes on the card!"),
|
||||
arg_lit0(NULL, "allnight", "Loops the loop for 10 times, recommended loop value of 5000."),
|
||||
arg_lit0(NULL, "sim", "Runs a simulation based on the card's CSN assuming standard key."),
|
||||
arg_lit0(NULL, "est", "Estimates the key updates based on the card's CSN assuming standard key."),
|
||||
arg_param_end
|
||||
};
|
||||
CLIExecWithReturn(ctx, Cmd, argtable, false);
|
||||
|
||||
+17
-6
@@ -7202,7 +7202,8 @@ static int CmdHf14AGen3Block(const char *Cmd) {
|
||||
" - You can specify part of manufacturer block as\n"
|
||||
" 4/7-bytes for UID change only\n"
|
||||
"\n"
|
||||
"NOTE: BCC, SAK, ATQA will be calculated automatically"
|
||||
"NOTE: BCC and ATQA will be calculated automatically\n"
|
||||
"SAK will be automatically set to default values if not specified"
|
||||
,
|
||||
"hf mf gen3blk --> print current data\n"
|
||||
"hf mf gen3blk -d 01020304 --> set 4 byte uid\n"
|
||||
@@ -9883,6 +9884,7 @@ static int CmdHF14AMfISEN(const char *Cmd) {
|
||||
arg_rem("FM11RF08S specific options:", "Incompatible with above options, except -k; output in JSON"),
|
||||
arg_lit0(NULL, "collect_fm11rf08s", "collect all nT/{nT}/par_err."),
|
||||
arg_lit0(NULL, "collect_fm11rf08s_with_data", "collect all nT/{nT}/par_err and data blocks."),
|
||||
arg_lit0(NULL, "collect_fm11rf08s_without_backdoor", "collect all nT/{nT}/par_err without backdoor. Requires first auth keytype and block"),
|
||||
arg_str0("f", "file", "<fn>", "Specify a filename for collected data"),
|
||||
arg_param_end
|
||||
};
|
||||
@@ -9954,9 +9956,18 @@ static int CmdHF14AMfISEN(const char *Cmd) {
|
||||
if (collect_fm11rf08s_with_data) {
|
||||
collect_fm11rf08s = 1;
|
||||
}
|
||||
bool collect_fm11rf08s_without_backdoor = arg_get_lit(ctx, 23);
|
||||
if (collect_fm11rf08s_without_backdoor) {
|
||||
collect_fm11rf08s = 1;
|
||||
}
|
||||
if (collect_fm11rf08s_with_data && collect_fm11rf08s_without_backdoor) {
|
||||
CLIParserFree(ctx);
|
||||
PrintAndLogEx(WARNING, "Don't mix with_data and without_backdoor options");
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
int fnlen = 0;
|
||||
char filename[FILE_PATH_SIZE] = {0};
|
||||
CLIParamStrToBuf(arg_get_str(ctx, 23), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen);
|
||||
CLIParamStrToBuf(arg_get_str(ctx, 24), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen);
|
||||
|
||||
CLIParserFree(ctx);
|
||||
|
||||
@@ -10005,10 +10016,10 @@ static int CmdHF14AMfISEN(const char *Cmd) {
|
||||
|
||||
if (collect_fm11rf08s) {
|
||||
uint64_t t1 = msclock();
|
||||
uint32_t flags = collect_fm11rf08s_with_data;
|
||||
SendCommandMIX(CMD_HF_MIFARE_ACQ_STATIC_ENCRYPTED_NONCES, flags, 0, 0, key, sizeof(key));
|
||||
if (WaitForResponseTimeout(CMD_HF_MIFARE_STATIC_ENCRYPTED_NONCE, &resp, 1000)) {
|
||||
if (resp.status == PM3_ESOFT) {
|
||||
uint32_t flags = collect_fm11rf08s_with_data | (collect_fm11rf08s_without_backdoor << 1);
|
||||
SendCommandMIX(CMD_HF_MIFARE_ACQ_STATIC_ENCRYPTED_NONCES, flags, blockn, keytype, key, sizeof(key));
|
||||
if (WaitForResponseTimeout(CMD_ACK, &resp, 1000)) {
|
||||
if (resp.oldarg[0] != PM3_SUCCESS) {
|
||||
return NONCE_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -333,10 +333,12 @@ static int CmdHFMFPInfo(const char *Cmd) {
|
||||
|
||||
// version check
|
||||
uint8_t version[30] = {0};
|
||||
uint8_t uid7b[7] = {0};
|
||||
int version_len = sizeof(version);
|
||||
if (get_plus_version(version, &version_len) == PM3_SUCCESS) {
|
||||
plus_print_version(version);
|
||||
supportVersion = true;
|
||||
memcpy(uid7b, version + 14, 7);
|
||||
} else {
|
||||
// info about 14a part, historical bytes.
|
||||
infoHF14A(false, false, false);
|
||||
@@ -346,7 +348,11 @@ static int CmdHFMFPInfo(const char *Cmd) {
|
||||
uint8_t signature[56] = {0};
|
||||
int signature_len = sizeof(signature);
|
||||
if (get_plus_signature(signature, &signature_len) == PM3_SUCCESS) {
|
||||
plus_print_signature(card.uid, card.uidlen, signature, signature_len);
|
||||
if (supportVersion) {
|
||||
plus_print_signature(uid7b, 7, signature, signature_len);
|
||||
} else {
|
||||
plus_print_signature(card.uid, card.uidlen, signature, signature_len);
|
||||
}
|
||||
supportSignature = true;
|
||||
}
|
||||
|
||||
|
||||
+123
-9
@@ -1540,21 +1540,13 @@ static int ulev1_print_version(uint8_t *data) {
|
||||
}
|
||||
|
||||
static int ntag_print_counter(void) {
|
||||
// NTAG has one counter/tearing. At address 0x02.
|
||||
// NTAG has one counter. At address 0x02. With no tearing.
|
||||
PrintAndLogEx(NORMAL, "");
|
||||
PrintAndLogEx(INFO, "--- " _CYAN_("Tag Counter"));
|
||||
uint8_t tear[1] = {0};
|
||||
uint8_t counter[3] = {0, 0, 0};
|
||||
uint16_t len;
|
||||
len = ulev1_readTearing(0x02, tear, sizeof(tear));
|
||||
(void)len;
|
||||
len = ulev1_readCounter(0x02, counter, sizeof(counter));
|
||||
(void)len;
|
||||
PrintAndLogEx(INFO, " [02]: %s", sprint_hex(counter, 3));
|
||||
PrintAndLogEx(SUCCESS, " - %02X tearing ( %s )"
|
||||
, tear[0]
|
||||
, (tear[0] == 0xBD) ? _GREEN_("ok") : _RED_("fail")
|
||||
);
|
||||
return len;
|
||||
}
|
||||
|
||||
@@ -5833,6 +5825,127 @@ out:
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
static int CmdHF14AMfUIncr(const char *Cmd) {
|
||||
CLIParserContext *ctx;
|
||||
CLIParserInit(&ctx, "hf mfu incr",
|
||||
"Increment a MIFARE Ultralight Ev1 counter\n"
|
||||
"Will read but not increment counter if NTAG is detected",
|
||||
"hf mfu incr -c 0 -v 1337\n"
|
||||
"hf mfu incr -c 2 -v 0 -p FFFFFFFF");
|
||||
void *argtable[] = {
|
||||
arg_param_begin,
|
||||
arg_int1("c", "cnt", "<dec>", "Counter index from 0"),
|
||||
arg_int1("v", "val", "<dec>", "Value to increment by (0-16777215)"),
|
||||
arg_str0("p", "pwd", "<hex>", "PWD to authenticate with"),
|
||||
arg_param_end
|
||||
};
|
||||
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
||||
|
||||
uint8_t counter = arg_get_int_def(ctx, 1, 3);
|
||||
uint32_t value = arg_get_u32_def(ctx, 2, 16777216);
|
||||
|
||||
int pwd_len;
|
||||
uint8_t pwd[4] = { 0x00 };
|
||||
CLIGetHexWithReturn(ctx, 3, pwd, &pwd_len);
|
||||
bool has_key = false;
|
||||
if (pwd_len) {
|
||||
has_key = true;
|
||||
if (pwd_len != 4) {
|
||||
PrintAndLogEx(WARNING, "incorrect PWD length");
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
}
|
||||
|
||||
CLIParserFree(ctx);
|
||||
|
||||
if (counter > 2) {
|
||||
PrintAndLogEx(WARNING, "Counter index must be in range 0-2");
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
if (value > 16777215) {
|
||||
PrintAndLogEx(WARNING, "Value to increment must be in range 0-16777215");
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
|
||||
uint8_t increment_cmd[6] = { MIFARE_ULEV1_INCR_CNT, counter, 0x00, 0x00, 0x00, 0x00 };
|
||||
|
||||
for (uint8_t i = 0; i < 3; i++) {
|
||||
increment_cmd[i + 2] = (value >> (8 * i)) & 0xff;
|
||||
}
|
||||
|
||||
iso14a_card_select_t card;
|
||||
if (ul_select(&card) == false) {
|
||||
PrintAndLogEx(FAILED, "failed to select card, exiting...");
|
||||
return PM3_ESOFT;
|
||||
}
|
||||
|
||||
uint64_t tagtype = GetHF14AMfU_Type();
|
||||
uint64_t tags_with_counter_ul = MFU_TT_UL_EV1_48 | MFU_TT_UL_EV1_128 | MFU_TT_UL_EV1;
|
||||
uint64_t tags_with_counter_ntag = MFU_TT_NTAG_213 | MFU_TT_NTAG_213_F | MFU_TT_NTAG_213_C | MFU_TT_NTAG_213_TT | MFU_TT_NTAG_215 | MFU_TT_NTAG_216;
|
||||
if ((tagtype & (tags_with_counter_ul | tags_with_counter_ntag)) == 0) {
|
||||
PrintAndLogEx(WARNING, "tag type does not have counters");
|
||||
DropField();
|
||||
return PM3_ESOFT;
|
||||
}
|
||||
|
||||
bool is_ntag = (tagtype & tags_with_counter_ntag) != 0;
|
||||
if (is_ntag && (counter != 2)) {
|
||||
PrintAndLogEx(WARNING, "NTAG only has one counter at index 2");
|
||||
DropField();
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
|
||||
uint8_t pack[4] = { 0, 0, 0, 0 };
|
||||
if (has_key) {
|
||||
if (ulev1_requestAuthentication(pwd, pack, sizeof(pack)) == PM3_EWRONGANSWER) {
|
||||
PrintAndLogEx(FAILED, "authentication failed UL-EV1/NTAG");
|
||||
DropField();
|
||||
return PM3_ESOFT;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t current_counter[3] = { 0, 0, 0 };
|
||||
int len = ulev1_readCounter(counter, current_counter, sizeof(current_counter));
|
||||
if (len != sizeof(current_counter)) {
|
||||
PrintAndLogEx(FAILED, "failed to read old counter");
|
||||
if (is_ntag) {
|
||||
PrintAndLogEx(HINT, "NTAG detected, try reading with PWD");
|
||||
}
|
||||
DropField();
|
||||
return PM3_ESOFT;
|
||||
}
|
||||
|
||||
uint32_t current_counter_num = current_counter[0] | (current_counter[1] << 8) | (current_counter[2] << 16);
|
||||
PrintAndLogEx(INFO, "Current counter... " _GREEN_("%8d") " - " _GREEN_("%s"), current_counter_num, sprint_hex(current_counter, 3));
|
||||
|
||||
if ((tagtype & tags_with_counter_ntag) != 0) {
|
||||
PrintAndLogEx(WARNING, "NTAG detected, unable to manually increment counter");
|
||||
DropField();
|
||||
return PM3_ESOFT;
|
||||
}
|
||||
|
||||
uint8_t resp[1] = { 0x00 };
|
||||
if (ul_send_cmd_raw(increment_cmd, sizeof(increment_cmd), resp, sizeof(resp)) < 0) {
|
||||
PrintAndLogEx(FAILED, "failed to increment counter");
|
||||
DropField();
|
||||
return PM3_ESOFT;
|
||||
}
|
||||
|
||||
uint8_t new_counter[3] = { 0, 0, 0 };
|
||||
int new_len = ulev1_readCounter(counter, new_counter, sizeof(new_counter));
|
||||
if (new_len != sizeof(current_counter)) {
|
||||
PrintAndLogEx(FAILED, "failed to read new counter");
|
||||
DropField();
|
||||
return PM3_ESOFT;
|
||||
}
|
||||
|
||||
uint32_t new_counter_num = new_counter[0] | (new_counter[1] << 8) | (new_counter[2] << 16);
|
||||
PrintAndLogEx(INFO, "New counter....... " _GREEN_("%8d") " - " _GREEN_("%s"), new_counter_num, sprint_hex(new_counter, 3));
|
||||
|
||||
DropField();
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
static command_t CommandTable[] = {
|
||||
{"help", CmdHelp, AlwaysAvailable, "This help"},
|
||||
{"list", CmdHF14AMfuList, AlwaysAvailable, "List MIFARE Ultralight / NTAG history"},
|
||||
@@ -5845,6 +5958,7 @@ static command_t CommandTable[] = {
|
||||
{"cauth", CmdHF14AMfUCAuth, IfPm3Iso14443a, "Ultralight-C - Authentication"},
|
||||
{"setpwd", CmdHF14AMfUCSetPwd, IfPm3Iso14443a, "Ultralight-C - Set 3DES key"},
|
||||
{"dump", CmdHF14AMfUDump, IfPm3Iso14443a, "Dump MIFARE Ultralight family tag to binary file"},
|
||||
{"incr", CmdHF14AMfUIncr, IfPm3Iso14443a, "Increments Ev1/NTAG counter"},
|
||||
{"info", CmdHF14AMfUInfo, IfPm3Iso14443a, "Tag information"},
|
||||
{"ndefread", CmdHF14MfuNDEFRead, IfPm3Iso14443a, "Prints NDEF records from card"},
|
||||
{"rdbl", CmdHF14AMfURdBl, IfPm3Iso14443a, "Read block"},
|
||||
|
||||
@@ -732,6 +732,7 @@ static int CmdEM410xClone(const char *Cmd) {
|
||||
|
||||
packet.cmd = HTSF_82xx;
|
||||
memcpy(packet.pwd, "\xBB\xDD\x33\x99", HITAGS_PAGE_SIZE);
|
||||
packet.mode = HITAGS_UID_REQ_FADV;
|
||||
SendCommandNG(CMD_LF_HITAGS_WRITE, (uint8_t *)&packet, sizeof(packet));
|
||||
if (WaitForResponseTimeout(CMD_LF_HITAGS_WRITE, &resp, 4000) == false) {
|
||||
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
|
||||
|
||||
@@ -444,6 +444,96 @@ static int CmdLFHitagSRead(const char *Cmd) {
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
static int CmdLFHitagSDump(const char *Cmd) {
|
||||
|
||||
CLIParserContext *ctx;
|
||||
CLIParserInit(&ctx, "lf hitag hts dump",
|
||||
"Read all Hitag S memory and save to file\n"
|
||||
" Crypto mode: \n"
|
||||
" - key format ISK high + ISK low\n"
|
||||
" - default key 4F4E4D494B52 (ONMIKR)\n\n"
|
||||
" 8268/8310 password mode: \n"
|
||||
" - default password BBDD3399\n",
|
||||
"lf hitag hts dump --82xx -k BBDD3399 -> pwd mode\n"
|
||||
"lf hitag hts dump --crypto -> use def crypto\n"
|
||||
"lf hitag hts dump -k 4F4E4D494B52 -> crypto mode\n"
|
||||
"lf hitag hts dump --nrar 0102030411223344\n"
|
||||
);
|
||||
|
||||
void *argtable[] = {
|
||||
arg_param_begin,
|
||||
arg_lit0("8", "82xx", "8268/8310 mode"),
|
||||
arg_str0(NULL, "nrar", "<hex>", "nonce / answer writer, 8 hex bytes"),
|
||||
arg_lit0(NULL, "crypto", "crypto mode"),
|
||||
arg_str0("k", "key", "<hex>", "pwd or key, 4 or 6 hex bytes"),
|
||||
arg_int0("m", "mode", "<dec>", "response protocol mode. 0 (Standard 00110), 1 (Advanced 11000), 2 (Advanced 11001), 3 (Fast Advanced 11010) (def: 3)"),
|
||||
arg_str0("f", "file", "<fn>", "specify file name"),
|
||||
arg_lit0(NULL, "ns", "no save to file"),
|
||||
arg_param_end
|
||||
};
|
||||
CLIExecWithReturn(ctx, Cmd, argtable, false);
|
||||
|
||||
lf_hitag_data_t packet;
|
||||
memset(&packet, 0, sizeof(packet));
|
||||
|
||||
if (process_hitags_common_args(ctx, &packet) < 0) {
|
||||
CLIParserFree(ctx);
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
|
||||
int fnlen = 0;
|
||||
char filename[FILE_PATH_SIZE] = {0};
|
||||
CLIParamStrToBuf(arg_get_str(ctx, 6), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen);
|
||||
|
||||
bool nosave = arg_get_lit(ctx, 7);
|
||||
CLIParserFree(ctx);
|
||||
|
||||
// read all pages
|
||||
packet.page = 0;
|
||||
packet.page_count = 0;
|
||||
|
||||
clearCommandBuffer();
|
||||
SendCommandNG(CMD_LF_HITAGS_READ, (uint8_t *) &packet, sizeof(packet));
|
||||
|
||||
PacketResponseNG resp;
|
||||
if (WaitForResponseTimeout(CMD_LF_HITAGS_READ, &resp, 5000) == false) {
|
||||
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
|
||||
return PM3_ETIMEOUT;
|
||||
}
|
||||
|
||||
if (resp.status != PM3_SUCCESS) {
|
||||
print_error(resp.reason);
|
||||
return PM3_ESOFT;
|
||||
}
|
||||
|
||||
lf_hts_read_response_t *card = (lf_hts_read_response_t *)resp.data.asBytes;
|
||||
|
||||
const int hts_mem_sizes[] = {1, 8, 64, 64};
|
||||
int mem_size = hts_mem_sizes[card->config_page.s.MEMT] * HITAGS_PAGE_SIZE;
|
||||
|
||||
hitags_config_t config = card->config_page.s;
|
||||
PrintAndLogEx(NORMAL, "");
|
||||
PrintAndLogEx(INFO, "--- " _CYAN_("Tag Information") " ---------------------------");
|
||||
hitags_config_print(config);
|
||||
|
||||
if (nosave) {
|
||||
PrintAndLogEx(NORMAL, "");
|
||||
PrintAndLogEx(INFO, "Called with no save option");
|
||||
PrintAndLogEx(NORMAL, "");
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
if (fnlen < 1) {
|
||||
char *fptr = filename;
|
||||
fptr += snprintf(filename, sizeof(filename), "lf-hitags-");
|
||||
FillFileNameByUID(fptr, card->pages[HITAGS_UID_PADR], "-dump", HITAGS_PAGE_SIZE);
|
||||
}
|
||||
|
||||
pm3_save_dump(filename, (uint8_t *)card->pages, mem_size, jsfHitag);
|
||||
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
static int CmdLFHitagSWrite(const char *Cmd) {
|
||||
CLIParserContext *ctx;
|
||||
CLIParserInit(&ctx, "lf hitag hts wrbl",
|
||||
@@ -615,6 +705,7 @@ static command_t CommandTable[] = {
|
||||
{"-----------", CmdHelp, IfPm3Hitag, "----------------------- " _CYAN_("General") " ------------------------"},
|
||||
{"reader", CmdLFHitagSReader, IfPm3Hitag, "Act like a Hitag S reader"},
|
||||
{"rdbl", CmdLFHitagSRead, IfPm3Hitag, "Read Hitag S page"},
|
||||
{"dump", CmdLFHitagSDump, IfPm3Hitag, "Dump Hitag S pages to a file"},
|
||||
{"wrbl", CmdLFHitagSWrite, IfPm3Hitag, "Write Hitag S page"},
|
||||
{"-----------", CmdHelp, IfPm3Hitag, "----------------------- " _CYAN_("Simulation") " -----------------------"},
|
||||
{"sim", CmdLFHitagSSim, IfPm3Hitag, "Simulate Hitag S transponder"},
|
||||
|
||||
@@ -1222,14 +1222,17 @@ static int DesfireAuthenticateEV1(DesfireContext_t *dctx, DesfireSecureChannel s
|
||||
|
||||
// - Encrypt our response
|
||||
if (secureChannel == DACd40) {
|
||||
// Original DESFire (MF3ICD40) silicon can only do encryption operations, so all PCD
|
||||
// side operations must be decrypt, even when encrypting when doing D40 compatible
|
||||
// secure channel operations
|
||||
memset(IV, 0, DESFIRE_MAX_CRYPTO_BLOCK_SIZE);
|
||||
DesfireCryptoEncDecEx(dctx, DCOMainKey, RndA, rndlen, encRndA, true, true, IV);
|
||||
DesfireCryptoEncDecEx(dctx, DCOMainKey, RndA, rndlen, encRndA, true, false, IV);
|
||||
|
||||
memcpy(both, encRndA, rndlen);
|
||||
bin_xor(rotRndB, encRndA, rndlen);
|
||||
|
||||
memset(IV, 0, DESFIRE_MAX_CRYPTO_BLOCK_SIZE);
|
||||
DesfireCryptoEncDecEx(dctx, DCOMainKey, rotRndB, rndlen, encRndB, true, true, IV);
|
||||
DesfireCryptoEncDecEx(dctx, DCOMainKey, rotRndB, rndlen, encRndB, true, false, IV);
|
||||
|
||||
memcpy(both + rndlen, encRndB, rndlen);
|
||||
} else if (secureChannel == DACEV1) {
|
||||
|
||||
@@ -313,6 +313,10 @@ static void DesfireSecureChannelEncodeD40(DesfireContext_t *ctx, uint8_t cmd, ui
|
||||
size_t srcmaclen = padded_data_length(srcdatalen - hdrlen, desfire_get_key_block_length(ctx->keyType));
|
||||
|
||||
uint8_t mac[32] = {0};
|
||||
PrintAndLogEx(DEBUG, "MACing");
|
||||
// Even though original DESFire (MF3ICD40) silicon can only encrypt which means normally
|
||||
// every PCD operation must be decrypt, verifying a MAC involves the same operation on both
|
||||
// sides so this is still encrypt here
|
||||
DesfireCryptoEncDecEx(ctx, DCOSessionKeyMac, data, srcmaclen, NULL, true, true, mac);
|
||||
|
||||
if (DesfireEV1D40TransmitMAC(ctx, cmd)) {
|
||||
@@ -889,4 +893,3 @@ bool PrintChannelModeWarning(uint8_t cmd, DesfireSecureChannel secureChannel, De
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user