From ded7e727d03342ade0acf9c32ec42d5d8eda2d63 Mon Sep 17 00:00:00 2001 From: Astra <93453568+Astrrra@users.noreply.github.com> Date: Tue, 27 Dec 2022 11:14:03 +0200 Subject: [PATCH] [FL-3060] New MFC Bruteforce animation (#2190) * Change the wording in the headers * Add support for text in the progress bar * New MFC key bruteforce screen * Typo fix * nfc: rename Flipper Dict to System Dict * elements: fix types * Display the correct key attack sector Co-authored-by: gornekich Co-authored-by: Aleksandr Kutuzov --- .../main/nfc/scenes/nfc_scene_extra_actions.c | 2 +- .../scenes/nfc_scene_mf_classic_dict_attack.c | 19 +++++-- .../nfc/scenes/nfc_scene_mf_classic_keys.c | 8 +-- applications/main/nfc/scenes/nfc_scene_read.c | 2 +- applications/main/nfc/views/dict_attack.c | 50 ++++++++++++++++--- applications/main/nfc/views/dict_attack.h | 4 ++ applications/services/gui/canvas.h | 1 + applications/services/gui/elements.c | 25 ++++++++++ applications/services/gui/elements.h | 17 +++++++ firmware/targets/f7/api_symbols.csv | 5 +- lib/nfc/helpers/mf_classic_dict.c | 4 +- lib/nfc/helpers/mf_classic_dict.h | 2 +- lib/nfc/nfc_device.h | 3 +- lib/nfc/nfc_worker.c | 8 +++ lib/nfc/nfc_worker.h | 3 ++ 15 files changed, 130 insertions(+), 23 deletions(-) diff --git a/applications/main/nfc/scenes/nfc_scene_extra_actions.c b/applications/main/nfc/scenes/nfc_scene_extra_actions.c index fc6021d7..717e8efc 100644 --- a/applications/main/nfc/scenes/nfc_scene_extra_actions.c +++ b/applications/main/nfc/scenes/nfc_scene_extra_actions.c @@ -43,7 +43,7 @@ bool nfc_scene_extra_actions_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { if(event.event == SubmenuIndexMfClassicKeys) { - if(mf_classic_dict_check_presence(MfClassicDictTypeFlipper)) { + if(mf_classic_dict_check_presence(MfClassicDictTypeSystem)) { scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicKeys); } else { scene_manager_next_scene(nfc->scene_manager, NfcSceneDictNotFound); diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_dict_attack.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_dict_attack.c index acb5b783..b82bf552 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_dict_attack.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_dict_attack.c @@ -53,10 +53,10 @@ static void nfc_scene_mf_classic_dict_attack_prepare_view(Nfc* nfc, DictAttackSt // Setup view if(state == DictAttackStateUserDictInProgress) { worker_state = NfcWorkerStateMfClassicDictAttack; - dict_attack_set_header(nfc->dict_attack, "Mf Classic User Dict."); + dict_attack_set_header(nfc->dict_attack, "MF Classic User Dictionary"); dict = mf_classic_dict_alloc(MfClassicDictTypeUser); - // If failed to load user dictionary - try flipper dictionary + // If failed to load user dictionary - try the system dictionary if(!dict) { FURI_LOG_E(TAG, "User dictionary not found"); state = DictAttackStateFlipperDictInProgress; @@ -64,11 +64,11 @@ static void nfc_scene_mf_classic_dict_attack_prepare_view(Nfc* nfc, DictAttackSt } if(state == DictAttackStateFlipperDictInProgress) { worker_state = NfcWorkerStateMfClassicDictAttack; - dict_attack_set_header(nfc->dict_attack, "Mf Classic Flipper Dict."); - dict = mf_classic_dict_alloc(MfClassicDictTypeFlipper); + dict_attack_set_header(nfc->dict_attack, "MF Classic System Dictionary"); + dict = mf_classic_dict_alloc(MfClassicDictTypeSystem); if(!dict) { FURI_LOG_E(TAG, "Flipper dictionary not found"); - // Pass through to let worker handle the failure + // Pass through to let the worker handle the failure } } // Free previous dictionary @@ -153,6 +153,15 @@ bool nfc_scene_mf_classic_dict_attack_on_event(void* context, SceneManagerEvent nfc_worker_stop(nfc->worker); consumed = true; } + } else if(event.event == NfcWorkerEventKeyAttackStart) { + dict_attack_set_key_attack( + nfc->dict_attack, + true, + nfc->dev->dev_data.mf_classic_dict_attack_data.current_sector); + } else if(event.event == NfcWorkerEventKeyAttackStop) { + dict_attack_set_key_attack(nfc->dict_attack, false, 0); + } else if(event.event == NfcWorkerEventKeyAttackNextSector) { + dict_attack_inc_key_attack_current_sector(nfc->dict_attack); } } else if(event.type == SceneManagerEventTypeBack) { scene_manager_next_scene(nfc->scene_manager, NfcSceneExitConfirm); diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_keys.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys.c index dee9553d..8a7dc2c1 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_keys.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys.c @@ -12,7 +12,7 @@ void nfc_scene_mf_classic_keys_on_enter(void* context) { // Load flipper dict keys total uint32_t flipper_dict_keys_total = 0; - MfClassicDict* dict = mf_classic_dict_alloc(MfClassicDictTypeFlipper); + MfClassicDict* dict = mf_classic_dict_alloc(MfClassicDictTypeSystem); if(dict) { flipper_dict_keys_total = mf_classic_dict_get_total_keys(dict); mf_classic_dict_free(dict); @@ -26,11 +26,11 @@ void nfc_scene_mf_classic_keys_on_enter(void* context) { } widget_add_string_element( - nfc->widget, 0, 0, AlignLeft, AlignTop, FontPrimary, "Mifare Classic Keys"); + nfc->widget, 0, 0, AlignLeft, AlignTop, FontPrimary, "MIFARE Classic Keys"); char temp_str[32]; - snprintf(temp_str, sizeof(temp_str), "Flipper list: %lu", flipper_dict_keys_total); + snprintf(temp_str, sizeof(temp_str), "System dict: %lu", flipper_dict_keys_total); widget_add_string_element(nfc->widget, 0, 20, AlignLeft, AlignTop, FontSecondary, temp_str); - snprintf(temp_str, sizeof(temp_str), "User list: %lu", user_dict_keys_total); + snprintf(temp_str, sizeof(temp_str), "User dict: %lu", user_dict_keys_total); widget_add_string_element(nfc->widget, 0, 32, AlignLeft, AlignTop, FontSecondary, temp_str); widget_add_button_element( nfc->widget, GuiButtonTypeCenter, "Add", nfc_scene_mf_classic_keys_widget_callback, nfc); diff --git a/applications/main/nfc/scenes/nfc_scene_read.c b/applications/main/nfc/scenes/nfc_scene_read.c index 2607cbd8..4252883b 100644 --- a/applications/main/nfc/scenes/nfc_scene_read.c +++ b/applications/main/nfc/scenes/nfc_scene_read.c @@ -91,7 +91,7 @@ bool nfc_scene_read_on_event(void* context, SceneManagerEvent event) { DOLPHIN_DEED(DolphinDeedNfcReadSuccess); consumed = true; } else if(event.event == NfcWorkerEventReadMfClassicDictAttackRequired) { - if(mf_classic_dict_check_presence(MfClassicDictTypeFlipper)) { + if(mf_classic_dict_check_presence(MfClassicDictTypeSystem)) { scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicDictAttack); } else { scene_manager_next_scene(nfc->scene_manager, NfcSceneDictNotFound); diff --git a/applications/main/nfc/views/dict_attack.c b/applications/main/nfc/views/dict_attack.c index c5f55ae7..5ae204a0 100644 --- a/applications/main/nfc/views/dict_attack.c +++ b/applications/main/nfc/views/dict_attack.c @@ -24,6 +24,8 @@ typedef struct { uint8_t keys_found; uint16_t dict_keys_total; uint16_t dict_keys_current; + bool is_key_attack; + uint8_t key_attack_current_sector; } DictAttackViewModel; static void dict_attack_draw_callback(Canvas* canvas, void* model) { @@ -36,10 +38,19 @@ static void dict_attack_draw_callback(Canvas* canvas, void* model) { canvas, 64, 23, AlignCenter, AlignTop, "Make sure the tag is\npositioned correctly."); } else if(m->state == DictAttackStateRead) { char draw_str[32] = {}; - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned( - canvas, 64, 2, AlignCenter, AlignTop, furi_string_get_cstr(m->header)); canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned( + canvas, 64, 0, AlignCenter, AlignTop, furi_string_get_cstr(m->header)); + if(m->is_key_attack) { + snprintf( + draw_str, + sizeof(draw_str), + "Reuse key check for sector: %d", + m->key_attack_current_sector); + } else { + snprintf(draw_str, sizeof(draw_str), "Unlocking sector: %d", m->sector_current); + } + canvas_draw_str_aligned(canvas, 0, 10, AlignLeft, AlignTop, draw_str); float dict_progress = m->dict_keys_total == 0 ? 0 : (float)(m->dict_keys_current) / (float)(m->dict_keys_total); @@ -49,13 +60,14 @@ static void dict_attack_draw_callback(Canvas* canvas, void* model) { if(progress > 1.0) { progress = 1.0; } - elements_progress_bar(canvas, 5, 15, 120, progress); + snprintf(draw_str, sizeof(draw_str), "%d/%d", m->dict_keys_current, m->dict_keys_total); + elements_progress_bar_with_text(canvas, 0, 20, 128, dict_progress, draw_str); canvas_set_font(canvas, FontSecondary); snprintf(draw_str, sizeof(draw_str), "Keys found: %d/%d", m->keys_found, m->keys_total); - canvas_draw_str_aligned(canvas, 1, 28, AlignLeft, AlignTop, draw_str); + canvas_draw_str_aligned(canvas, 0, 33, AlignLeft, AlignTop, draw_str); snprintf( draw_str, sizeof(draw_str), "Sectors Read: %d/%d", m->sectors_read, m->sectors_total); - canvas_draw_str_aligned(canvas, 1, 40, AlignLeft, AlignTop, draw_str); + canvas_draw_str_aligned(canvas, 0, 43, AlignLeft, AlignTop, draw_str); } elements_button_center(canvas, "Skip"); } @@ -113,6 +125,7 @@ void dict_attack_reset(DictAttack* dict_attack) { model->keys_found = 0; model->dict_keys_total = 0; model->dict_keys_current = 0; + model->is_key_attack = false; furi_string_reset(model->header); }, false); @@ -235,3 +248,28 @@ void dict_attack_inc_current_dict_key(DictAttack* dict_attack, uint16_t keys_tri }, true); } + +void dict_attack_set_key_attack(DictAttack* dict_attack, bool is_key_attack, uint8_t sector) { + furi_assert(dict_attack); + with_view_model( + dict_attack->view, + DictAttackViewModel * model, + { + model->is_key_attack = is_key_attack; + model->key_attack_current_sector = sector; + }, + true); +} + +void dict_attack_inc_key_attack_current_sector(DictAttack* dict_attack) { + furi_assert(dict_attack); + with_view_model( + dict_attack->view, + DictAttackViewModel * model, + { + if(model->key_attack_current_sector < model->sectors_total) { + model->key_attack_current_sector++; + } + }, + true); +} diff --git a/applications/main/nfc/views/dict_attack.h b/applications/main/nfc/views/dict_attack.h index 684f17f0..2839534a 100644 --- a/applications/main/nfc/views/dict_attack.h +++ b/applications/main/nfc/views/dict_attack.h @@ -38,3 +38,7 @@ 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); + +void dict_attack_set_key_attack(DictAttack* dict_attack, bool is_key_attack, uint8_t sector); + +void dict_attack_inc_key_attack_current_sector(DictAttack* dict_attack); \ No newline at end of file diff --git a/applications/services/gui/canvas.h b/applications/services/gui/canvas.h index a3df5adc..c4eb8649 100644 --- a/applications/services/gui/canvas.h +++ b/applications/services/gui/canvas.h @@ -17,6 +17,7 @@ extern "C" { typedef enum { ColorWhite = 0x00, ColorBlack = 0x01, + ColorXOR = 0x02, } Color; /** Fonts enumeration */ diff --git a/applications/services/gui/elements.c b/applications/services/gui/elements.c index cd4c105a..e22889bf 100644 --- a/applications/services/gui/elements.c +++ b/applications/services/gui/elements.c @@ -41,6 +41,31 @@ void elements_progress_bar(Canvas* canvas, uint8_t x, uint8_t y, uint8_t width, canvas_draw_box(canvas, x + 1, y + 1, progress_length, height - 2); } +void elements_progress_bar_with_text( + Canvas* canvas, + uint8_t x, + uint8_t y, + uint8_t width, + float progress, + const char* text) { + furi_assert(canvas); + furi_assert((progress >= 0.0f) && (progress <= 1.0f)); + uint8_t height = 11; + + uint8_t progress_length = roundf(progress * (width - 2)); + + canvas_set_color(canvas, ColorWhite); + canvas_draw_box(canvas, x + 1, y + 1, width - 2, height - 2); + canvas_set_color(canvas, ColorBlack); + canvas_draw_rframe(canvas, x, y, width, height, 3); + + canvas_draw_box(canvas, x + 1, y + 1, progress_length, height - 2); + + canvas_set_color(canvas, ColorXOR); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned(canvas, x + width / 2, y + 2, AlignCenter, AlignTop, text); +} + void elements_scrollbar_pos( Canvas* canvas, uint8_t x, diff --git a/applications/services/gui/elements.h b/applications/services/gui/elements.h index 162f0d41..48533513 100644 --- a/applications/services/gui/elements.h +++ b/applications/services/gui/elements.h @@ -31,6 +31,23 @@ extern "C" { */ void elements_progress_bar(Canvas* canvas, uint8_t x, uint8_t y, uint8_t width, float progress); +/** Draw progress bar with text. + * + * @param canvas Canvas instance + * @param x progress bar position on X axis + * @param y progress bar position on Y axis + * @param width progress bar width + * @param progress progress (0.0 - 1.0) + * @param text text to draw + */ +void elements_progress_bar_with_text( + Canvas* canvas, + uint8_t x, + uint8_t y, + uint8_t width, + float progress, + const char* text); + /** Draw scrollbar on canvas at specific position. * * @param canvas Canvas instance diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 8503a838..72d44b50 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,11.2,, +Version,+,11.3,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -562,8 +562,8 @@ Function,+,ble_glue_wait_for_c2_start,_Bool,int32_t Function,-,bsearch,void*,"const void*, const void*, size_t, size_t, __compar_fn_t" Function,+,bt_disconnect,void,Bt* Function,+,bt_forget_bonded_devices,void,Bt* -Function,+,bt_keys_storage_set_storage_path,void,"Bt*, const char*" Function,+,bt_keys_storage_set_default_path,void,Bt* +Function,+,bt_keys_storage_set_storage_path,void,"Bt*, const char*" Function,+,bt_set_profile,_Bool,"Bt*, BtProfile" Function,+,bt_set_status_changed_callback,void,"Bt*, BtStatusChangedCallback, void*" Function,+,buffered_file_stream_alloc,Stream*,Storage* @@ -754,6 +754,7 @@ Function,+,elements_multiline_text,void,"Canvas*, uint8_t, uint8_t, const char*" Function,+,elements_multiline_text_aligned,void,"Canvas*, uint8_t, uint8_t, Align, Align, const char*" Function,+,elements_multiline_text_framed,void,"Canvas*, uint8_t, uint8_t, const char*" Function,+,elements_progress_bar,void,"Canvas*, uint8_t, uint8_t, uint8_t, float" +Function,+,elements_progress_bar_with_text,void,"Canvas*, uint8_t, uint8_t, uint8_t, float, const char*" Function,+,elements_scrollable_text_line,void,"Canvas*, uint8_t, uint8_t, uint8_t, FuriString*, size_t, _Bool" Function,+,elements_scrollbar,void,"Canvas*, uint16_t, uint16_t" Function,+,elements_scrollbar_pos,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint16_t, uint16_t" diff --git a/lib/nfc/helpers/mf_classic_dict.c b/lib/nfc/helpers/mf_classic_dict.c index 98076479..93098d40 100644 --- a/lib/nfc/helpers/mf_classic_dict.c +++ b/lib/nfc/helpers/mf_classic_dict.c @@ -20,7 +20,7 @@ bool mf_classic_dict_check_presence(MfClassicDictType dict_type) { Storage* storage = furi_record_open(RECORD_STORAGE); bool dict_present = false; - if(dict_type == MfClassicDictTypeFlipper) { + if(dict_type == MfClassicDictTypeSystem) { dict_present = storage_common_stat(storage, MF_CLASSIC_DICT_FLIPPER_PATH, NULL) == FSE_OK; } else if(dict_type == MfClassicDictTypeUser) { dict_present = storage_common_stat(storage, MF_CLASSIC_DICT_USER_PATH, NULL) == FSE_OK; @@ -42,7 +42,7 @@ MfClassicDict* mf_classic_dict_alloc(MfClassicDictType dict_type) { bool dict_loaded = false; do { - if(dict_type == MfClassicDictTypeFlipper) { + if(dict_type == MfClassicDictTypeSystem) { if(!buffered_file_stream_open( dict->stream, MF_CLASSIC_DICT_FLIPPER_PATH, diff --git a/lib/nfc/helpers/mf_classic_dict.h b/lib/nfc/helpers/mf_classic_dict.h index 5b0ee312..3b2d560a 100644 --- a/lib/nfc/helpers/mf_classic_dict.h +++ b/lib/nfc/helpers/mf_classic_dict.h @@ -8,7 +8,7 @@ typedef enum { MfClassicDictTypeUser, - MfClassicDictTypeFlipper, + MfClassicDictTypeSystem, MfClassicDictTypeUnitTest, } MfClassicDictType; diff --git a/lib/nfc/nfc_device.h b/lib/nfc/nfc_device.h index 55ee4ac4..8b2e6e5b 100644 --- a/lib/nfc/nfc_device.h +++ b/lib/nfc/nfc_device.h @@ -18,7 +18,7 @@ extern "C" { #define NFC_DEV_NAME_MAX_LEN 22 #define NFC_READER_DATA_MAX_SIZE 64 -#define NFC_DICT_KEY_BATCH_SIZE 50 +#define NFC_DICT_KEY_BATCH_SIZE 10 #define NFC_APP_EXTENSION ".nfc" #define NFC_APP_SHADOW_EXTENSION ".shd" @@ -48,6 +48,7 @@ typedef struct { typedef struct { MfClassicDict* dict; + uint8_t current_sector; } NfcMfClassicDictAttackData; typedef enum { diff --git a/lib/nfc/nfc_worker.c b/lib/nfc/nfc_worker.c index 0ffe1d07..4a66593c 100644 --- a/lib/nfc/nfc_worker.c +++ b/lib/nfc/nfc_worker.c @@ -573,17 +573,24 @@ static void nfc_worker_mf_classic_key_attack( FuriHalNfcTxRxContext* tx_rx, uint16_t start_sector) { furi_assert(nfc_worker); + furi_assert(nfc_worker->callback); bool card_found_notified = true; bool card_removed_notified = false; 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); furi_assert(start_sector < total_sectors); + nfc_worker->callback(NfcWorkerEventKeyAttackStart, nfc_worker->context); + // Check every sector's A and B keys with the given key for(size_t i = start_sector; i < total_sectors; i++) { + nfc_worker->callback(NfcWorkerEventKeyAttackNextSector, nfc_worker->context); + dict_attack_data->current_sector = i; furi_hal_nfc_sleep(); if(furi_hal_nfc_activate_nfca(200, NULL)) { furi_hal_nfc_sleep(); @@ -632,6 +639,7 @@ static void nfc_worker_mf_classic_key_attack( } if(nfc_worker->state != NfcWorkerStateMfClassicDictAttack) break; } + nfc_worker->callback(NfcWorkerEventKeyAttackStop, nfc_worker->context); } void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker) { diff --git a/lib/nfc/nfc_worker.h b/lib/nfc/nfc_worker.h index fdcaa72f..ce542828 100644 --- a/lib/nfc/nfc_worker.h +++ b/lib/nfc/nfc_worker.h @@ -55,6 +55,9 @@ typedef enum { NfcWorkerEventNewDictKeyBatch, NfcWorkerEventFoundKeyA, NfcWorkerEventFoundKeyB, + NfcWorkerEventKeyAttackStart, + NfcWorkerEventKeyAttackStop, + NfcWorkerEventKeyAttackNextSector, // Write Mifare Classic events NfcWorkerEventWrongCard,