Merge branch 'master' into master

Signed-off-by: Iceman <iceman@iuse.se>
This commit is contained in:
Iceman 2024-12-07 05:18:41 +01:00 committed by GitHub
commit 92e8dea093
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
31 changed files with 954 additions and 328 deletions

View file

@ -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)

View file

@ -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: {

View file

@ -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);

View file

@ -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);

View file

@ -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);
}

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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")

View file

@ -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()

View file

@ -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",

View file

@ -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);

View file

@ -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;
}
}

View file

@ -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;
}

View file

@ -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"},

View file

@ -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.");

View file

@ -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"},

View file

@ -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) {

View file

@ -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;
}

View file

@ -696,14 +696,18 @@ void print_progress(uint64_t count, uint64_t max, barMode_t style) {
max = (count > max) ? count : max;
#if defined(HAVE_READLINE)
static int prev_cols = 0;
int rows;
rl_reset_screen_size(); // refresh Readline idea of the actual screen width
rl_get_screen_size(&rows, &cols);
int tmp_cols;
rl_get_screen_size(NULL, &tmp_cols);
// if cols==0: impossible to get screen size, e.g. when scripted
if (tmp_cols != 0) {
// don't call it if cols==0, it would segfault
rl_reset_screen_size(); // refresh Readline idea of the actual screen width
rl_get_screen_size(NULL, &cols);
if (cols < 36)
return;
if (cols < 36)
return;
}
(void) rows;
if (prev_cols > cols) {
PrintAndLogEx(NORMAL, _CLEAR_ _TOP_ "");
}

View file

@ -554,8 +554,8 @@ size_t concatbits(uint8_t *dest, int dest_offset, const uint8_t *src, int src_of
end = nbits;
step = 1;
} else {
i = nbits;
end = 0;
i = nbits - 1;
end = -1;
step = -1;
}

View file

@ -366,7 +366,6 @@ void Flashmem_print_status(void) {
);
}
Dbprintf(" Device.................. " _YELLOW_("%s"), spi_flash_data.device);
Dbprintf(" Memory size............. " _YELLOW_("%d kB (%d pages * 64k)"), spi_flash_pages64k * 64, spi_flash_pages64k);
uint8_t uid[8] = {0, 0, 0, 0, 0, 0, 0, 0};
@ -442,21 +441,13 @@ bool FlashDetect(void) {
} else {
if (g_dbglevel > 3) Dbprintf("Flash_ReadID failed reading Mfr/Dev (0x90)");
}
// default device is 'unknown'
spi_flash_data.device = SpiFlashTable[0].device;
// Check JEDEC data is valid, compare the reported device types and then calculate the number of pages
// It is covering the most (known) cases of devices but probably there are vendors with different data
// They will be handled when there is such cases
if (ret) {
for (int i = 0; i < ARRAYLEN(SpiFlashTable); i++) {
if (SpiFlashTable[i].manufacturer_id == spi_flash_data.manufacturer_id) {
if (SpiFlashTable[i].jedec_id == spi_flash_data.jedec_id) {
spi_flash_pages64k = SpiFlashTable[i].pages64k;
spi_flash_data.device = SpiFlashTable[i].device;
break;
}
if (SpiFlashTable[i].device_id == spi_flash_data.device_id) {
spi_flash_data.device = SpiFlashTable[i].device;
break;
}
if (spi_flash_data.jedec_id > 0 && spi_flash_data.jedec_id < 0xFFFF) {
if (((spi_flash_data.device_id + 1) & 0x0F) == (spi_flash_data.jedec_id & 0x000F)) {
spi_flash_pages64k = 1 << (spi_flash_data.jedec_id & 0x000F);
}
}
}

View file

@ -140,36 +140,8 @@ typedef struct {
uint8_t manufacturer_id;
uint8_t device_id;
uint16_t jedec_id;
uint8_t pages64k;
char *device;
} spi_flash_t;
static const spi_flash_t SpiFlashTable[] = {
// first element is the default of 4 * 64kB pages (256kB)
{ 0x00, 0x00, 0x0000, 4, "unknown" }, // 256k
// Manufacturer: Puya
{ 0x85, 0x00, 0x6015, 32, "P25Q16H" }, // 2048k
/// Manufacturer: Renesas
{ 0x1F, 0x46, 0x0000, 32, "AT25XE161D" }, // 2048k
{ 0x1F, 0x47, 0x0000, 64, "AT25XE321D" }, // 4096k
// Manufacturer: Winbond
{ 0xEF, 0x00, 0x3012, 4, "W25X20BV" }, // 256k
{ 0xEF, 0x00, 0x3013, 8, "W25X40BV" }, // 512k
{ 0xEF, 0x00, 0x4013, 8, "W25Q40BV" }, // 512k
{ 0xEF, 0x00, 0x4014, 16, "W25Q80BV" }, // 1024k
{ 0xEF, 0x14, 0x4015, 32, "W25Q16BV" }, // 2048k
{ 0xEF, 0x15, 0x4016, 64, "W25Q32BV" }, // 4096k
{ 0xEF, 0x21, 0x7022, 4, "W25Q02JV" },
// identified by Manufacturer /Device ID
// { 0xEF, 0x05, 0x0000, 1, "Winbond!!!" },
{ 0xEF, 0x10, 0x0000, 2, "W25*10BV!!!" }, // 128k
{ 0xEF, 0x11, 0x0000, 4, "W25*20BV" }, // 256k
{ 0xEF, 0x12, 0x0000, 8, "W25*40BV" }, // 512k
{ 0xEF, 0x13, 0x0000, 16, "W25*80BV" } // 1024k
};
extern uint8_t spi_flash_pages64k;
bool FlashDetect(void);

View file

@ -36,22 +36,22 @@ Therefore a flash address can be interpreted as such:
Page 0:
* available for user data
* to dump it: `mem dump f page0_dump o 0 l 65536`
* to dump it: `mem dump -f page0_dump -o 0 -l 65536`
* to erase it: `mem wipe p 0`
Page 1:
* available for user data
* to dump it: `mem dump f page1_dump o 65536 l 65536`
* to dump it: `mem dump -f page1_dump -o 65536 -l 65536`
* to erase it: `mem wipe p 1`
Page 2:
* available for user data
* to dump it: `mem dump f page2_dump o 131072 l 65536`
* to dump it: `mem dump -f page2_dump -o 131072 -l 65536`
* to erase it: `mem wipe p 2`
Page 3:
* used by Proxmark3 RDV4 specific functions: flash signature and keys dictionaries, see below for details
* to dump it: `mem dump f page3_dump o 196608 l 65536`
* to dump it: `mem dump -f page3_dump -o 196608 -l 65536`
* to erase it:
* **Beware** it will erase your flash signature so better to back it up first as you won't be able to regenerate it by yourself!
* edit the source code to enable Page 3 as a valid input in the `mem wipe` command.
@ -63,8 +63,8 @@ Page 3:
Page3 is used as follows by the Proxmark3 RDV4 firmware:
* **MF_KEYS**
* offset: page 3 sector 9 (0x9) @ 3*0x10000+9*0x1000=0x39000
* length: 2 sectors
* offset: page 3 sector 5 (0x5) @ 3*0x10000+5*0x1000=0x35000
* length: 6 sectors
* **ICLASS_KEYS**
* offset: page 3 sector 11 (0xB) @ 3*0x10000+11*0x1000=0x3B000

View file

@ -117,6 +117,17 @@ or
proxmark3 /dev/ttyACM0 --flash --unlock-bootloader --image /tmp/my-bootrom.elf --image /tmp/my-fullimage.elf
```
## Updating SPI flash structure and contents (RDV4.x, some PM3 Easy variants)
^[Top](#top)
For the devices equipped with external SPI flash memory chip in some cases it might be essential to update the memory structure as well as to upload new keys from the dictionaries. To do so execute following command inside the client:
```
[usb] pm3 --> script run init_rdv4
```
For more details prease refer to [this doc](./2_Configuration-and-Verification.md).
### The button trick
^[Top](#top)

45
fpga/xc2s50-5-tq144.ucf Normal file
View file

@ -0,0 +1,45 @@
# See the schematic for the pin assignment.
NET "adc_d<0>" LOC = "P54" ;
NET "adc_d<1>" LOC = "P57" ;
NET "adc_d<2>" LOC = "P59" ;
NET "adc_d<3>" LOC = "P60" ;
NET "adc_d<4>" LOC = "P62" ;
NET "adc_d<5>" LOC = "P63" ;
NET "adc_d<6>" LOC = "P65" ;
NET "adc_d<7>" LOC = "P67" ;
#NET "cross_hi" LOC = "P88" ;
#NET "miso" LOC = "P40" ;
NET "adc_clk" LOC = "P75" ;
NET "adc_noe" LOC = "P74" ;
NET "ck_1356meg" LOC = "P15" ;
NET "ck_1356megb" LOC = "P12" ;
NET "cross_lo" LOC = "P19" ;
NET "dbg" LOC = "P112" ;
NET "mosi" LOC = "P80" ;
NET "ncs" LOC = "P79" ;
NET "pck0" LOC = "P91" ;
NET "pwr_hi" LOC = "P31" ;
NET "pwr_lo" LOC = "P30" ;
NET "pwr_oe1" LOC = "P28" ;
NET "pwr_oe2" LOC = "P27" ;
NET "pwr_oe3" LOC = "P26" ;
NET "pwr_oe4" LOC = "P21" ;
NET "spck" LOC = "P88" ;
NET "ssp_clk" LOC = "P43" ;
NET "ssp_din" LOC = "P99" ;
NET "ssp_dout" LOC = "P94" ;
NET "ssp_frame" LOC = "P100" ;
# definition of Clock nets:
NET "ck_1356meg" TNM_NET = "clk_net_1356" ;
NET "ck_1356megb" TNM_NET = "clk_net_1356b";
NET "pck0" TNM_NET = "clk_net_pck0" ;
NET "spck" TNM_NET = "clk_net_spck" ;
# Timing specs of clock nets:
TIMEGRP "clk_net_1356_all" = "clk_net_1356" "clk_net_1356b" ;
TIMESPEC "TS_1356MHz" = PERIOD "clk_net_1356_all" 74 ns HIGH 37 ns ;
TIMESPEC "TS_24MHz" = PERIOD "clk_net_pck0" 42 ns HIGH 21 ns ;
TIMESPEC "TS_4MHz" = PERIOD "clk_net_spck" 250 ns HIGH 125 ns ;

View file

@ -161,7 +161,7 @@ typedef struct {
uint8_t logdata_1[4];
uint8_t nonce[4];
//Hitag s section
// Hitag S section
uint8_t mode;
} PACKED lf_hitag_data_t;

View file

@ -28,7 +28,7 @@
// 0x3E000 - 1 4kb sector = settings
// 0x3D000 - 1 4kb sector = default T55XX keys dictionary
// 0x3B000 - 1 4kb sector = default ICLASS keys dictionary
// 0x38000 - 3 4kb sectors = default MFC keys dictionary
// 0x35000 - 6 4kb sectors = default MFC keys dictionary
//
#ifndef FLASH_MEM_BLOCK_SIZE
# define FLASH_MEM_BLOCK_SIZE 256
@ -95,9 +95,9 @@
# define DEFAULT_ICLASS_KEYS_OFFSET_P(p64k) (DEFAULT_T55XX_KEYS_OFFSET_P(p64k) - DEFAULT_ICLASS_KEYS_LEN)
#endif
// Reserved space for MIFARE Keys = 12 kb
// Reserved space for MIFARE Keys = 24 kb
#ifndef DEFAULT_MF_KEYS_OFFSET
# define DEFAULT_MF_KEYS_LEN (0x3000)
# define DEFAULT_MF_KEYS_LEN (0x6000)
# define DEFAULT_MF_KEYS_OFFSET (DEFAULT_ICLASS_KEYS_OFFSET - DEFAULT_MF_KEYS_LEN)
# define DEFAULT_MF_KEYS_MAX ((DEFAULT_MF_KEYS_LEN - 2) / 6)
#endif

View file

@ -12,12 +12,13 @@ from colors import color
debug = False
def guess_curvename(signature):
siglen = (len(signature) // 2) & 0xfe
if siglen == 32:
curves = ["secp128r1", "secp128r2"]
elif siglen == 48:
curves = ["secp192k1", "secp192r1"]
curves = ["secp192k1", "prime192v1"]
elif siglen == 56:
curves = ["secp224k1", "secp224r1"]
elif siglen == 64:
@ -31,6 +32,7 @@ def guess_curvename(signature):
raise ValueError("Unsupported signature size %s" % lenstr)
return curves
def recover(data, signature, curvename, alghash=None):
recovered = set()
try:
@ -60,6 +62,7 @@ def recover(data, signature, curvename, alghash=None):
pass
return recovered
def recover_multiple(uids, sigs, curvename, alghash=None):
recovered = set()
assert len(uids) == len(sigs)
@ -82,6 +85,7 @@ def recover_multiple(uids, sigs, curvename, alghash=None):
recovered &= recovered_tmp
return recovered
def selftests():
tests = [
{'name': "Mifare Ultralight EV1",
@ -158,16 +162,16 @@ def selftests():
# 'samples': ["aa", "DF0E506DFF8FCFC4B7B979D917644445F1230D2C7CDC342AFA842CA240C210BE7275F62073A9670F2DCEFC602CBEE771C2B4CD4A04F3D1EA11F49ABDF7E8B721"],
# 'pk': ""},
{'name': "MIFARE Plus Trojka",
# uses secp224r1, None,
'samples': ["04B59F6A226F82", "6F577EB7F570D74DB6250477427F68A0088762BD318767537122919A7916597149F9D16D8B135E9BF826FB28AE293F3168661CD4A049FAED",
"04B44A82D80F92", "A0868ECF26733D3C3C838D055968B4559F77693CC3E346E3A4741BC826801F8360FD88857BEC440AAD3A21153D64302DEB6F5ED40B15C3F7"],
'pk': "040F732E0EA7DF2B38F791BF89425BF7DCDF3EE4D976669E3831F324FF15751BD52AFF1782F72FF2731EEAD5F63ABE7D126E03C856FFB942AF"},
# uses secp224r1, None,
'samples': ["04B59F6A226F82", "6F577EB7F570D74DB6250477427F68A0088762BD318767537122919A7916597149F9D16D8B135E9BF826FB28AE293F3168661CD4A049FAED",
"04B44A82D80F92", "A0868ECF26733D3C3C838D055968B4559F77693CC3E346E3A4741BC826801F8360FD88857BEC440AAD3A21153D64302DEB6F5ED40B15C3F7"],
'pk': "040F732E0EA7DF2B38F791BF89425BF7DCDF3EE4D976669E3831F324FF15751BD52AFF1782F72FF2731EEAD5F63ABE7D126E03C856FFB942AF"},
# {'name': "MIFARE Ultralight AES",
# uses NID_secp192r1, OpenSSL doesn't support it. This is commented out until that day.
# 'samples': ["045E4CC2451390", "C9BBDA1B99EB6634CDFD8E3251AC5C4742EA5FA507B8A8A8B39B19AB7340D173331589C54C56C49F0CCA6DDBAC1E492A",
# "043F88C2451390", "5C2055A7373F119C3FDD9843020B06AA0E6DE18C16496C425C4AD971A50F05FA1A67B9E39CA60C355EEEEBF8214A84A5"],
# 'pk': "0453BF8C49B7BD9FE3207A91513B9C1D238ECAB07186B772104AB535F7D3AE63CF7C7F3DD0D169DA3E99E43C6399621A86"},
{'name': "MIFARE Ultralight AES",
# uses prime192v1, None,
'samples': ["045E4CC2451390", "C9BBDA1B99EB6634CDFD8E3251AC5C4742EA5FA507B8A8A8B39B19AB7340D173331589C54C56C49F0CCA6DDBAC1E492A",
"043F88C2451390", "5C2055A7373F119C3FDD9843020B06AA0E6DE18C16496C425C4AD971A50F05FA1A67B9E39CA60C355EEEEBF8214A84A5"],
'pk': "0453BF8C49B7BD9FE3207A91513B9C1D238ECAB07186B772104AB535F7D3AE63CF7C7F3DD0D169DA3E99E43C6399621A86"},
{'name': "MIFARE Classic / QL88",
'samples': ["30933C61", "AEA4DD0B800FAC63D4DE08EE91F4650ED825FD6B4D7DEEE98DBC9BAE10BE003E",
@ -234,13 +238,15 @@ def selftests():
print("Tests: ( %s )" % [fail, ok][succeeded])
print("")
if __name__ == "__main__":
if len(sys.argv) == 2 and sys.argv[1] == "selftests":
selftests()
exit(0)
if len(sys.argv) < 3 or len(sys.argv) % 2 == 0:
print("Usage: \n%s UID SIGN [UID SIGN] [...]" % sys.argv[0])
print("Example: \n%s 04ee45daa34084 ebb6102bff74b087d18a57a54bc375159a04ea9bc61080b7f4a85afe1587d73b" % sys.argv[0])
print("Example: \n%s 04ee45daa34084 ebb6102bff74b087d18a57a54bc375159a04ea9bc61080b7f4a85afe1587d73b"
% sys.argv[0])
exit(1)
uids, sigs = sys.argv[1:][::2], sys.argv[1:][1::2]
once = True