From 3ee93e1a820fa0401b4de49de0e1050b83e53399 Mon Sep 17 00:00:00 2001 From: Fedor Indutny <238531+indutny@users.noreply.github.com> Date: Wed, 3 Aug 2022 10:07:35 -0700 Subject: [PATCH] nfc: make dict attack more interactive (#1462) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * nfc: deduplify dict attack worker code * nfc: make dict attack more interactive Co-authored-by: gornekich Co-authored-by: あく --- .../scenes/nfc_scene_mf_classic_dict_attack.c | 41 +++++++++++++++++-- applications/nfc/views/dict_attack.c | 37 ++++++++++++++++- applications/nfc/views/dict_attack.h | 4 ++ lib/nfc/nfc_device.h | 11 ++++- lib/nfc/nfc_worker.c | 35 ++++++++-------- lib/nfc/nfc_worker.h | 4 +- lib/nfc/nfc_worker_i.h | 3 +- 7 files changed, 107 insertions(+), 28 deletions(-) diff --git a/applications/nfc/scenes/nfc_scene_mf_classic_dict_attack.c b/applications/nfc/scenes/nfc_scene_mf_classic_dict_attack.c index 0736f0f1..d821c182 100644 --- a/applications/nfc/scenes/nfc_scene_mf_classic_dict_attack.c +++ b/applications/nfc/scenes/nfc_scene_mf_classic_dict_attack.c @@ -1,5 +1,7 @@ #include "../nfc_i.h" +#define TAG "NfcMfClassicDictAttack" + typedef enum { DictAttackStateIdle, DictAttackStateUserDictInProgress, @@ -32,7 +34,9 @@ static void nfc_scene_mf_classic_dict_attack_update_view(Nfc* nfc) { static void nfc_scene_mf_classic_dict_attack_prepare_view(Nfc* nfc, DictAttackState state) { MfClassicData* data = &nfc->dev->dev_data.mf_classic_data; + NfcMfClassicDictAttackData* dict_attack_data = &nfc->dev->dev_data.mf_classic_dict_attack_data; NfcWorkerState worker_state = NfcWorkerStateReady; + MfClassicDict* dict = NULL; // Identify scene state if(state == DictAttackStateIdle) { @@ -47,16 +51,36 @@ static void nfc_scene_mf_classic_dict_attack_prepare_view(Nfc* nfc, DictAttackSt // Setup view if(state == DictAttackStateUserDictInProgress) { - worker_state = NfcWorkerStateMfClassicUserDictAttack; + worker_state = NfcWorkerStateMfClassicDictAttack; dict_attack_set_header(nfc->dict_attack, "Mf Classic User Dict."); - } else if(state == DictAttackStateFlipperDictInProgress) { - worker_state = NfcWorkerStateMfClassicFlipperDictAttack; - dict_attack_set_header(nfc->dict_attack, "Mf Classic Flipper Dict."); + dict = mf_classic_dict_alloc(MfClassicDictTypeUser); + + // If failed to load user dictionary - try flipper dictionary + if(!dict) { + FURI_LOG_E(TAG, "User dictionary not found"); + state = DictAttackStateFlipperDictInProgress; + } } + if(state == DictAttackStateFlipperDictInProgress) { + worker_state = NfcWorkerStateMfClassicDictAttack; + dict_attack_set_header(nfc->dict_attack, "Mf Classic Flipper Dict."); + dict = mf_classic_dict_alloc(MfClassicDictTypeFlipper); + if(!dict) { + FURI_LOG_E(TAG, "Flipper dictionary not found"); + // Pass through to let worker handle the failure + } + } + // Free previous dictionary + if(dict_attack_data->dict) { + mf_classic_dict_free(dict_attack_data->dict); + } + dict_attack_data->dict = dict; scene_manager_set_scene_state(nfc->scene_manager, NfcSceneMfClassicDictAttack, state); dict_attack_set_callback(nfc->dict_attack, nfc_dict_attack_dict_attack_result_callback, nfc); dict_attack_set_current_sector(nfc->dict_attack, 0); dict_attack_set_card_detected(nfc->dict_attack, data->type); + dict_attack_set_total_dict_keys( + nfc->dict_attack, dict ? mf_classic_dict_get_total_keys(dict) : 0); nfc_scene_mf_classic_dict_attack_update_view(nfc); nfc_worker_start( nfc->worker, worker_state, &nfc->dev->dev_data, nfc_dict_attack_worker_callback, nfc); @@ -112,6 +136,10 @@ bool nfc_scene_mf_classic_dict_attack_on_event(void* context, SceneManagerEvent nfc_scene_mf_classic_dict_attack_update_view(nfc); dict_attack_inc_current_sector(nfc->dict_attack); consumed = true; + } else if(event.event == NfcWorkerEventNewDictKeyBatch) { + nfc_scene_mf_classic_dict_attack_update_view(nfc); + dict_attack_inc_current_dict_key(nfc->dict_attack, NFC_DICT_KEY_BATCH_SIZE); + consumed = true; } else if(event.event == NfcCustomEventDictAttackSkip) { if(state == DictAttackStateUserDictInProgress) { nfc_worker_stop(nfc->worker); @@ -130,8 +158,13 @@ bool nfc_scene_mf_classic_dict_attack_on_event(void* context, SceneManagerEvent void nfc_scene_mf_classic_dict_attack_on_exit(void* context) { Nfc* nfc = context; + NfcMfClassicDictAttackData* dict_attack_data = &nfc->dev->dev_data.mf_classic_dict_attack_data; // Stop worker nfc_worker_stop(nfc->worker); + if(dict_attack_data->dict) { + mf_classic_dict_free(dict_attack_data->dict); + dict_attack_data->dict = NULL; + } dict_attack_reset(nfc->dict_attack); nfc_blink_stop(nfc); } diff --git a/applications/nfc/views/dict_attack.c b/applications/nfc/views/dict_attack.c index 25690005..b4674fd3 100644 --- a/applications/nfc/views/dict_attack.c +++ b/applications/nfc/views/dict_attack.c @@ -23,6 +23,8 @@ typedef struct { uint8_t sector_current; uint8_t keys_total; uint8_t keys_found; + uint16_t dict_keys_total; + uint16_t dict_keys_current; } DictAttackViewModel; static void dict_attack_draw_callback(Canvas* canvas, void* model) { @@ -38,8 +40,15 @@ static void dict_attack_draw_callback(Canvas* canvas, void* model) { canvas_set_font(canvas, FontPrimary); canvas_draw_str_aligned(canvas, 64, 2, AlignCenter, AlignTop, string_get_cstr(m->header)); canvas_set_font(canvas, FontSecondary); - float progress = - m->sectors_total == 0 ? 0 : (float)(m->sector_current) / (float)(m->sectors_total); + float dict_progress = m->dict_keys_total == 0 ? + 0 : + (float)(m->dict_keys_current) / (float)(m->dict_keys_total); + float progress = m->sectors_total == 0 ? 0 : + ((float)(m->sector_current) + dict_progress) / + (float)(m->sectors_total); + if(progress > 1.0) { + progress = 1.0; + } elements_progress_bar(canvas, 5, 15, 120, progress); canvas_set_font(canvas, FontSecondary); snprintf(draw_str, sizeof(draw_str), "Keys found: %d/%d", m->keys_found, m->keys_total); @@ -100,6 +109,8 @@ void dict_attack_reset(DictAttack* dict_attack) { model->sector_current = 0; model->keys_total = 0; model->keys_found = 0; + model->dict_keys_total = 0; + model->dict_keys_current = 0; string_reset(model->header); return false; }); @@ -171,6 +182,7 @@ void dict_attack_set_current_sector(DictAttack* dict_attack, uint8_t curr_sec) { with_view_model( dict_attack->view, (DictAttackViewModel * model) { model->sector_current = curr_sec; + model->dict_keys_current = 0; return true; }); } @@ -181,6 +193,7 @@ void dict_attack_inc_current_sector(DictAttack* dict_attack) { dict_attack->view, (DictAttackViewModel * model) { if(model->sector_current < model->sectors_total) { model->sector_current++; + model->dict_keys_current = 0; } return true; }); @@ -196,3 +209,23 @@ void dict_attack_inc_keys_found(DictAttack* dict_attack) { return true; }); } + +void dict_attack_set_total_dict_keys(DictAttack* dict_attack, uint16_t dict_keys_total) { + furi_assert(dict_attack); + with_view_model( + dict_attack->view, (DictAttackViewModel * model) { + model->dict_keys_total = dict_keys_total; + return true; + }); +} + +void dict_attack_inc_current_dict_key(DictAttack* dict_attack, uint16_t keys_tried) { + furi_assert(dict_attack); + with_view_model( + dict_attack->view, (DictAttackViewModel * model) { + if(model->dict_keys_current + keys_tried < model->dict_keys_total) { + model->dict_keys_current += keys_tried; + } + return true; + }); +} diff --git a/applications/nfc/views/dict_attack.h b/applications/nfc/views/dict_attack.h index 3f557b19..684f17f0 100644 --- a/applications/nfc/views/dict_attack.h +++ b/applications/nfc/views/dict_attack.h @@ -34,3 +34,7 @@ void dict_attack_set_current_sector(DictAttack* dict_attack, uint8_t curr_sec); void dict_attack_inc_current_sector(DictAttack* dict_attack); void dict_attack_inc_keys_found(DictAttack* dict_attack); + +void dict_attack_set_total_dict_keys(DictAttack* dict_attack, uint16_t dict_keys_total); + +void dict_attack_inc_current_dict_key(DictAttack* dict_attack, uint16_t keys_tried); diff --git a/lib/nfc/nfc_device.h b/lib/nfc/nfc_device.h index e1ff6d42..f9a0b24b 100644 --- a/lib/nfc/nfc_device.h +++ b/lib/nfc/nfc_device.h @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -13,6 +14,7 @@ #define NFC_DEV_NAME_MAX_LEN 22 #define NFC_READER_DATA_MAX_SIZE 64 +#define NFC_DICT_KEY_BATCH_SIZE 50 #define NFC_APP_FOLDER ANY_PATH("nfc") #define NFC_APP_EXTENSION ".nfc" @@ -41,10 +43,17 @@ typedef struct { uint16_t size; } NfcReaderRequestData; +typedef struct { + MfClassicDict* dict; +} NfcMfClassicDictAttackData; + typedef struct { FuriHalNfcDevData nfc_data; NfcProtocol protocol; - NfcReaderRequestData reader_data; + union { + NfcReaderRequestData reader_data; + NfcMfClassicDictAttackData mf_classic_dict_attack_data; + }; union { EmvData emv_data; MfUltralightData mf_ul_data; diff --git a/lib/nfc/nfc_worker.c b/lib/nfc/nfc_worker.c index 3a45c363..7c3c083b 100644 --- a/lib/nfc/nfc_worker.c +++ b/lib/nfc/nfc_worker.c @@ -101,10 +101,8 @@ int32_t nfc_worker_task(void* context) { nfc_worker_emulate_mf_ultralight(nfc_worker); } else if(nfc_worker->state == NfcWorkerStateMfClassicEmulate) { nfc_worker_emulate_mf_classic(nfc_worker); - } else if(nfc_worker->state == NfcWorkerStateMfClassicUserDictAttack) { - nfc_worker_mf_classic_dict_attack(nfc_worker, MfClassicDictTypeUser); - } else if(nfc_worker->state == NfcWorkerStateMfClassicFlipperDictAttack) { - nfc_worker_mf_classic_dict_attack(nfc_worker, MfClassicDictTypeFlipper); + } else if(nfc_worker->state == NfcWorkerStateMfClassicDictAttack) { + nfc_worker_mf_classic_dict_attack(nfc_worker); } furi_hal_nfc_sleep(); nfc_worker_change_state(nfc_worker, NfcWorkerStateReady); @@ -397,11 +395,13 @@ void nfc_worker_emulate_mf_ultralight(NfcWorker* nfc_worker) { } } -void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker, MfClassicDictType type) { +void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker) { furi_assert(nfc_worker); furi_assert(nfc_worker->callback); MfClassicData* data = &nfc_worker->dev_data->mf_classic_data; + NfcMfClassicDictAttackData* dict_attack_data = + &nfc_worker->dev_data->mf_classic_dict_attack_data; uint32_t total_sectors = mf_classic_get_total_sectors_num(data->type); uint64_t key = 0; FuriHalNfcTxRxContext tx_rx = {}; @@ -409,15 +409,17 @@ void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker, MfClassicDictType bool card_removed_notified = false; // Load dictionary - MfClassicDict* dict = mf_classic_dict_alloc(type); + MfClassicDict* dict = dict_attack_data->dict; if(!dict) { FURI_LOG_E(TAG, "Dictionary not found"); nfc_worker->callback(NfcWorkerEventNoDictFound, nfc_worker->context); - mf_classic_dict_free(dict); return; } - FURI_LOG_D(TAG, "Start Dictionary attack"); + FURI_LOG_D( + TAG, + "Start Dictionary attack, Key Count %d", + mf_classic_dict_get_total_keys(dict)); for(size_t i = 0; i < total_sectors; i++) { FURI_LOG_I(TAG, "Sector %d", i); nfc_worker->callback(NfcWorkerEventNewSector, nfc_worker->context); @@ -425,7 +427,11 @@ void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker, MfClassicDictType if(mf_classic_is_sector_read(data, i)) continue; bool is_key_a_found = mf_classic_is_key_found(data, i, MfClassicKeyA); bool is_key_b_found = mf_classic_is_key_found(data, i, MfClassicKeyB); + uint16_t key_index = 0; while(mf_classic_dict_get_next_key(dict, &key)) { + if(++key_index % NFC_DICT_KEY_BATCH_SIZE == 0) { + nfc_worker->callback(NfcWorkerEventNewDictKeyBatch, nfc_worker->context); + } furi_hal_nfc_sleep(); if(furi_hal_nfc_activate_nfca(200, NULL)) { furi_hal_nfc_sleep(); @@ -456,8 +462,7 @@ void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker, MfClassicDictType } } if(is_key_a_found && is_key_b_found) break; - if(!((nfc_worker->state == NfcWorkerStateMfClassicUserDictAttack) || - (nfc_worker->state == NfcWorkerStateMfClassicFlipperDictAttack))) + if(nfc_worker->state != NfcWorkerStateMfClassicDictAttack) break; } else { if(!card_removed_notified) { @@ -465,20 +470,16 @@ void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker, MfClassicDictType card_removed_notified = true; card_found_notified = false; } - if(!((nfc_worker->state == NfcWorkerStateMfClassicUserDictAttack) || - (nfc_worker->state == NfcWorkerStateMfClassicFlipperDictAttack))) + if(nfc_worker->state != NfcWorkerStateMfClassicDictAttack) break; } } - if(!((nfc_worker->state == NfcWorkerStateMfClassicUserDictAttack) || - (nfc_worker->state == NfcWorkerStateMfClassicFlipperDictAttack))) + if(nfc_worker->state != NfcWorkerStateMfClassicDictAttack) break; mf_classic_read_sector(&tx_rx, data, i); mf_classic_dict_rewind(dict); } - mf_classic_dict_free(dict); - if((nfc_worker->state == NfcWorkerStateMfClassicUserDictAttack) || - (nfc_worker->state == NfcWorkerStateMfClassicFlipperDictAttack)) { + if(nfc_worker->state == NfcWorkerStateMfClassicDictAttack) { nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context); } else { nfc_worker->callback(NfcWorkerEventAborted, nfc_worker->context); diff --git a/lib/nfc/nfc_worker.h b/lib/nfc/nfc_worker.h index f6df406b..a3326808 100755 --- a/lib/nfc/nfc_worker.h +++ b/lib/nfc/nfc_worker.h @@ -14,8 +14,7 @@ typedef enum { NfcWorkerStateUidEmulate, NfcWorkerStateMfUltralightEmulate, NfcWorkerStateMfClassicEmulate, - NfcWorkerStateMfClassicUserDictAttack, - NfcWorkerStateMfClassicFlipperDictAttack, + NfcWorkerStateMfClassicDictAttack, // Debug NfcWorkerStateEmulateApdu, NfcWorkerStateField, @@ -49,6 +48,7 @@ typedef enum { // Mifare Classic events NfcWorkerEventNoDictFound, NfcWorkerEventNewSector, + NfcWorkerEventNewDictKeyBatch, NfcWorkerEventFoundKeyA, NfcWorkerEventFoundKeyB, } NfcWorkerEvent; diff --git a/lib/nfc/nfc_worker_i.h b/lib/nfc/nfc_worker_i.h index f19f58d5..bb4c31dd 100644 --- a/lib/nfc/nfc_worker_i.h +++ b/lib/nfc/nfc_worker_i.h @@ -13,7 +13,6 @@ #include #include -#include "helpers/mf_classic_dict.h" #include "helpers/nfc_debug_pcap.h" struct NfcWorker { @@ -43,6 +42,6 @@ void nfc_worker_emulate_mf_ultralight(NfcWorker* nfc_worker); void nfc_worker_emulate_mf_classic(NfcWorker* nfc_worker); -void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker, MfClassicDictType type); +void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker); void nfc_worker_emulate_apdu(NfcWorker* nfc_worker);