[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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Astra
						Astra