[FL-2876] MFC Improvements Part 2/2 (#1868)
* Remove keys incorrectly added by the key cache * Improve responsiveness while checking for re-used keys and fix skipping keys when card is removed * Actually check if the card is completely read * Discard incorrect keys on a lower level * nfc: clean up Co-authored-by: gornekich <n.gorbadey@gmail.com>
This commit is contained in:
parent
e46e6f8ee9
commit
55f8beef9f
@ -191,7 +191,7 @@ static bool nfc_worker_read_mf_classic(NfcWorker* nfc_worker, FuriHalNfcTxRxCont
|
|||||||
uint8_t sectors_total =
|
uint8_t sectors_total =
|
||||||
mf_classic_get_total_sectors_num(nfc_worker->dev_data->mf_classic_data.type);
|
mf_classic_get_total_sectors_num(nfc_worker->dev_data->mf_classic_data.type);
|
||||||
FURI_LOG_I(TAG, "Read %d sectors out of %d total", sectors_read, sectors_total);
|
FURI_LOG_I(TAG, "Read %d sectors out of %d total", sectors_read, sectors_total);
|
||||||
read_success = (sectors_read == sectors_total);
|
read_success = mf_classic_is_card_read(&nfc_worker->dev_data->mf_classic_data);
|
||||||
}
|
}
|
||||||
} while(false);
|
} while(false);
|
||||||
|
|
||||||
@ -480,6 +480,9 @@ static void nfc_worker_mf_classic_key_attack(
|
|||||||
uint16_t start_sector) {
|
uint16_t start_sector) {
|
||||||
furi_assert(nfc_worker);
|
furi_assert(nfc_worker);
|
||||||
|
|
||||||
|
bool card_found_notified = true;
|
||||||
|
bool card_removed_notified = false;
|
||||||
|
|
||||||
MfClassicData* data = &nfc_worker->dev_data->mf_classic_data;
|
MfClassicData* data = &nfc_worker->dev_data->mf_classic_data;
|
||||||
uint32_t total_sectors = mf_classic_get_total_sectors_num(data->type);
|
uint32_t total_sectors = mf_classic_get_total_sectors_num(data->type);
|
||||||
|
|
||||||
@ -487,36 +490,52 @@ static void nfc_worker_mf_classic_key_attack(
|
|||||||
|
|
||||||
// Check every sector's A and B keys with the given key
|
// Check every sector's A and B keys with the given key
|
||||||
for(size_t i = start_sector; i < total_sectors; i++) {
|
for(size_t i = start_sector; i < total_sectors; i++) {
|
||||||
uint8_t block_num = mf_classic_get_sector_trailer_block_num_by_sector(i);
|
furi_hal_nfc_sleep();
|
||||||
if(mf_classic_is_sector_read(data, i)) continue;
|
if(furi_hal_nfc_activate_nfca(200, NULL)) {
|
||||||
if(!mf_classic_is_key_found(data, i, MfClassicKeyA)) {
|
furi_hal_nfc_sleep();
|
||||||
FURI_LOG_D(
|
if(!card_found_notified) {
|
||||||
TAG,
|
nfc_worker->callback(NfcWorkerEventCardDetected, nfc_worker->context);
|
||||||
"Trying A key for sector %d, key: %04lx%08lx",
|
card_found_notified = true;
|
||||||
i,
|
card_removed_notified = false;
|
||||||
(uint32_t)(key >> 32),
|
}
|
||||||
(uint32_t)key);
|
uint8_t block_num = mf_classic_get_sector_trailer_block_num_by_sector(i);
|
||||||
if(mf_classic_authenticate(tx_rx, block_num, key, MfClassicKeyA)) {
|
if(mf_classic_is_sector_read(data, i)) continue;
|
||||||
mf_classic_set_key_found(data, i, MfClassicKeyA, key);
|
if(!mf_classic_is_key_found(data, i, MfClassicKeyA)) {
|
||||||
FURI_LOG_D(TAG, "Key found");
|
FURI_LOG_D(
|
||||||
nfc_worker->callback(NfcWorkerEventFoundKeyA, nfc_worker->context);
|
TAG,
|
||||||
|
"Trying A key for sector %d, key: %04lx%08lx",
|
||||||
|
i,
|
||||||
|
(uint32_t)(key >> 32),
|
||||||
|
(uint32_t)key);
|
||||||
|
if(mf_classic_authenticate(tx_rx, block_num, key, MfClassicKeyA)) {
|
||||||
|
mf_classic_set_key_found(data, i, MfClassicKeyA, key);
|
||||||
|
FURI_LOG_D(TAG, "Key found");
|
||||||
|
nfc_worker->callback(NfcWorkerEventFoundKeyA, nfc_worker->context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!mf_classic_is_key_found(data, i, MfClassicKeyB)) {
|
||||||
|
FURI_LOG_D(
|
||||||
|
TAG,
|
||||||
|
"Trying B key for sector %d, key: %04lx%08lx",
|
||||||
|
i,
|
||||||
|
(uint32_t)(key >> 32),
|
||||||
|
(uint32_t)key);
|
||||||
|
if(mf_classic_authenticate(tx_rx, block_num, key, MfClassicKeyB)) {
|
||||||
|
mf_classic_set_key_found(data, i, MfClassicKeyB, key);
|
||||||
|
FURI_LOG_D(TAG, "Key found");
|
||||||
|
nfc_worker->callback(NfcWorkerEventFoundKeyB, nfc_worker->context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(mf_classic_is_sector_read(data, i)) continue;
|
||||||
|
mf_classic_read_sector(tx_rx, data, i);
|
||||||
|
} else {
|
||||||
|
if(!card_removed_notified) {
|
||||||
|
nfc_worker->callback(NfcWorkerEventNoCardDetected, nfc_worker->context);
|
||||||
|
card_removed_notified = true;
|
||||||
|
card_found_notified = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(!mf_classic_is_key_found(data, i, MfClassicKeyB)) {
|
|
||||||
FURI_LOG_D(
|
|
||||||
TAG,
|
|
||||||
"Trying B key for sector %d, key: %04lx%08lx",
|
|
||||||
i,
|
|
||||||
(uint32_t)(key >> 32),
|
|
||||||
(uint32_t)key);
|
|
||||||
if(mf_classic_authenticate(tx_rx, block_num, key, MfClassicKeyB)) {
|
|
||||||
mf_classic_set_key_found(data, i, MfClassicKeyB, key);
|
|
||||||
FURI_LOG_D(TAG, "Key found");
|
|
||||||
nfc_worker->callback(NfcWorkerEventFoundKeyB, nfc_worker->context);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(mf_classic_is_sector_read(data, i)) continue;
|
|
||||||
mf_classic_read_sector(tx_rx, data, i);
|
|
||||||
if(nfc_worker->state != NfcWorkerStateMfClassicDictAttack) break;
|
if(nfc_worker->state != NfcWorkerStateMfClassicDictAttack) break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -530,6 +549,7 @@ void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker) {
|
|||||||
&nfc_worker->dev_data->mf_classic_dict_attack_data;
|
&nfc_worker->dev_data->mf_classic_dict_attack_data;
|
||||||
uint32_t total_sectors = mf_classic_get_total_sectors_num(data->type);
|
uint32_t total_sectors = mf_classic_get_total_sectors_num(data->type);
|
||||||
uint64_t key = 0;
|
uint64_t key = 0;
|
||||||
|
uint64_t prev_key = 0;
|
||||||
FuriHalNfcTxRxContext tx_rx = {};
|
FuriHalNfcTxRxContext tx_rx = {};
|
||||||
bool card_found_notified = true;
|
bool card_found_notified = true;
|
||||||
bool card_removed_notified = false;
|
bool card_removed_notified = false;
|
||||||
@ -564,6 +584,7 @@ void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker) {
|
|||||||
nfc_worker->callback(NfcWorkerEventCardDetected, nfc_worker->context);
|
nfc_worker->callback(NfcWorkerEventCardDetected, nfc_worker->context);
|
||||||
card_found_notified = true;
|
card_found_notified = true;
|
||||||
card_removed_notified = false;
|
card_removed_notified = false;
|
||||||
|
nfc_worker_mf_classic_key_attack(nfc_worker, prev_key, &tx_rx, i);
|
||||||
}
|
}
|
||||||
FURI_LOG_D(
|
FURI_LOG_D(
|
||||||
TAG,
|
TAG,
|
||||||
@ -600,6 +621,7 @@ void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker) {
|
|||||||
}
|
}
|
||||||
if(nfc_worker->state != NfcWorkerStateMfClassicDictAttack) break;
|
if(nfc_worker->state != NfcWorkerStateMfClassicDictAttack) break;
|
||||||
}
|
}
|
||||||
|
memcpy(&prev_key, &key, sizeof(key));
|
||||||
}
|
}
|
||||||
if(nfc_worker->state != NfcWorkerStateMfClassicDictAttack) break;
|
if(nfc_worker->state != NfcWorkerStateMfClassicDictAttack) break;
|
||||||
mf_classic_read_sector(&tx_rx, data, i);
|
mf_classic_read_sector(&tx_rx, data, i);
|
||||||
|
@ -155,6 +155,16 @@ void mf_classic_set_key_found(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void mf_classic_set_key_not_found(MfClassicData* data, uint8_t sector_num, MfClassicKey key_type) {
|
||||||
|
furi_assert(data);
|
||||||
|
|
||||||
|
if(key_type == MfClassicKeyA) {
|
||||||
|
FURI_BIT_CLEAR(data->key_a_mask, sector_num);
|
||||||
|
} else if(key_type == MfClassicKeyB) {
|
||||||
|
FURI_BIT_CLEAR(data->key_b_mask, sector_num);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool mf_classic_is_sector_read(MfClassicData* data, uint8_t sector_num) {
|
bool mf_classic_is_sector_read(MfClassicData* data, uint8_t sector_num) {
|
||||||
furi_assert(data);
|
furi_assert(data);
|
||||||
|
|
||||||
@ -203,6 +213,18 @@ void mf_classic_get_read_sectors_and_keys(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool mf_classic_is_card_read(MfClassicData* data) {
|
||||||
|
furi_assert(data);
|
||||||
|
|
||||||
|
uint8_t sectors_total = mf_classic_get_total_sectors_num(data->type);
|
||||||
|
uint8_t sectors_read = 0;
|
||||||
|
uint8_t keys_found = 0;
|
||||||
|
mf_classic_get_read_sectors_and_keys(data, §ors_read, &keys_found);
|
||||||
|
bool card_read = (sectors_read == sectors_total) && (keys_found == sectors_total * 2);
|
||||||
|
|
||||||
|
return card_read;
|
||||||
|
}
|
||||||
|
|
||||||
static bool mf_classic_is_allowed_access_sector_trailer(
|
static bool mf_classic_is_allowed_access_sector_trailer(
|
||||||
MfClassicEmulator* emulator,
|
MfClassicEmulator* emulator,
|
||||||
uint8_t block_num,
|
uint8_t block_num,
|
||||||
@ -612,7 +634,15 @@ static bool mf_classic_read_sector_with_reader(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Auth to first block in sector
|
// Auth to first block in sector
|
||||||
if(!mf_classic_auth(tx_rx, first_block, key, key_type, crypto)) break;
|
if(!mf_classic_auth(tx_rx, first_block, key, key_type, crypto)) {
|
||||||
|
// Set key to MF_CLASSIC_NO_KEY to prevent further attempts
|
||||||
|
if(key_type == MfClassicKeyA) {
|
||||||
|
sector_reader->key_a = MF_CLASSIC_NO_KEY;
|
||||||
|
} else {
|
||||||
|
sector_reader->key_b = MF_CLASSIC_NO_KEY;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
sector->total_blocks = mf_classic_get_blocks_num_in_sector(sector_reader->sector_num);
|
sector->total_blocks = mf_classic_get_blocks_num_in_sector(sector_reader->sector_num);
|
||||||
|
|
||||||
// Read blocks
|
// Read blocks
|
||||||
@ -711,6 +741,13 @@ uint8_t mf_classic_update_card(FuriHalNfcTxRxContext* tx_rx, MfClassicData* data
|
|||||||
mf_classic_set_block_read(data, first_block + j, &temp_sector.block[j]);
|
mf_classic_set_block_read(data, first_block + j, &temp_sector.block[j]);
|
||||||
}
|
}
|
||||||
sectors_read++;
|
sectors_read++;
|
||||||
|
} else {
|
||||||
|
// Invalid key, set it to not found
|
||||||
|
if(key_a != MF_CLASSIC_NO_KEY) {
|
||||||
|
mf_classic_set_key_not_found(data, i, MfClassicKeyA);
|
||||||
|
} else {
|
||||||
|
mf_classic_set_key_not_found(data, i, MfClassicKeyB);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -98,12 +98,16 @@ void mf_classic_set_key_found(
|
|||||||
MfClassicKey key_type,
|
MfClassicKey key_type,
|
||||||
uint64_t key);
|
uint64_t key);
|
||||||
|
|
||||||
|
void mf_classic_set_key_not_found(MfClassicData* data, uint8_t sector_num, MfClassicKey key_type);
|
||||||
|
|
||||||
bool mf_classic_is_block_read(MfClassicData* data, uint8_t block_num);
|
bool mf_classic_is_block_read(MfClassicData* data, uint8_t block_num);
|
||||||
|
|
||||||
void mf_classic_set_block_read(MfClassicData* data, uint8_t block_num, MfClassicBlock* block_data);
|
void mf_classic_set_block_read(MfClassicData* data, uint8_t block_num, MfClassicBlock* block_data);
|
||||||
|
|
||||||
bool mf_classic_is_sector_read(MfClassicData* data, uint8_t sector_num);
|
bool mf_classic_is_sector_read(MfClassicData* data, uint8_t sector_num);
|
||||||
|
|
||||||
|
bool mf_classic_is_card_read(MfClassicData* data);
|
||||||
|
|
||||||
void mf_classic_get_read_sectors_and_keys(
|
void mf_classic_get_read_sectors_and_keys(
|
||||||
MfClassicData* data,
|
MfClassicData* data,
|
||||||
uint8_t* sectors_read,
|
uint8_t* sectors_read,
|
||||||
|
Loading…
Reference in New Issue
Block a user