Browse Source

Merge mifare_fuzzer from https://github.com/xMasterX/all-the-plugins

Willy-JL 2 years ago
parent
commit
b194de8b7b

+ 6 - 0
mifare_fuzzer/.gitignore

@@ -0,0 +1,6 @@
+dist/*
+.vscode
+.clang-format
+.editorconfig
+.env
+.ufbt

+ 14 - 6
mifare_fuzzer/README.md

@@ -2,20 +2,28 @@
 
 ### What
 This app allows you to fake Mifare UIDs.  
-It emulates only the UID of the card, it does not emulate the full card, but it seems to be enough...
+It can emulate either UIDs or full cards from your Flipper's storage.
 
-Currently it does support this kinds of card:
+Currently it supports this kinds of card:
 - Classic 1k
 - Classic 4k
 - Ultralight
 
 ### Install
-To compile you must be familiar with the Flipperzero firmware.
+#### FBT
+
 1. Checkout the Flipperzero firmware
-2. go to `applications/plugins/`
+2. Go to `applications/plugins/`
 3. `git clone` this repo inside directory
-4. return to main firmware directory with `cd ../..`
-5. run `fbt fap_mifare_fuzzer` to compile
+4. Return to main firmware directory with `cd ../..`
+5. Run `fbt fap_mifare_fuzzer` to compile
+
+#### uFBT (no firmware required)
+
+1. [Install uFBT](https://pypi.org/project/ufbt/)
+2. Open your app's directory in Terminal
+3. Deploy the app to your Flipper with `ufbt launch`
+
 
 ### License
 

+ 1 - 1
mifare_fuzzer/TODO.md

@@ -8,4 +8,4 @@
 
 - Add saving option
 
-- Emulate a full card and not only the UID
+- ~~Emulate a full card and not only the UID~~ 

+ 1 - 0
mifare_fuzzer/application.fam

@@ -4,6 +4,7 @@ App(
     apptype=FlipperAppType.EXTERNAL,
     entry_point="mifare_fuzzer_app",
     requires=[
+        "nfc",
         "storage",
         "gui",
     ],

+ 13 - 2
mifare_fuzzer/mifare_fuzzer.c

@@ -49,6 +49,9 @@ MifareFuzzerApp* mifare_fuzzer_alloc() {
     app->gui = furi_record_open(RECORD_GUI);
     view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
 
+    // Open Notifications record
+    app->notifications = furi_record_open(RECORD_NOTIFICATION);
+
     // view: select card type
     app->submenu_card = submenu_alloc();
     view_dispatcher_add_view(
@@ -80,7 +83,8 @@ MifareFuzzerApp* mifare_fuzzer_alloc() {
 
     // furi strings
     app->uid_str = furi_string_alloc();
-    app->file_path = furi_string_alloc();
+    app->uid_file_path = furi_string_alloc();
+    app->card_file_path = furi_string_alloc();
     app->app_folder = furi_string_alloc_set(MIFARE_FUZZER_APP_FOLDER);
 
     return app;
@@ -115,6 +119,10 @@ void mifare_fuzzer_free(MifareFuzzerApp* app) {
     furi_record_close(RECORD_GUI);
     app->gui = NULL;
 
+    // Notifications
+    furi_record_close(RECORD_NOTIFICATION);
+    app->notifications = NULL;
+
     // Worker
     //FURI_LOG_D(TAG, "mifare_fuzzer_free() :: Worker");
     mifare_fuzzer_worker_free(app->worker);
@@ -129,8 +137,11 @@ void mifare_fuzzer_free(MifareFuzzerApp* app) {
 
     // furi strings
     furi_string_free(app->uid_str);
-    furi_string_free(app->file_path);
+    furi_string_free(app->uid_file_path);
     furi_string_free(app->app_folder);
+    if(app->card_file_path != NULL) {
+        furi_string_free(app->card_file_path);
+    }
 
     // App
     //FURI_LOG_D(TAG, "mifare_fuzzer_free() :: App");

+ 1 - 0
mifare_fuzzer/mifare_fuzzer_custom_events.h

@@ -4,6 +4,7 @@ typedef enum MifareFuzzerEvent {
     MifareFuzzerEventClassic1k = 1,
     MifareFuzzerEventClassic4k,
     MifareFuzzerEventUltralight,
+    MifareFuzzerEventFile,
     MifareFuzzerEventTestValueAttack,
     MifareFuzzerEventRandomValuesAttack,
     MifareFuzzerEventLoadUIDsFromFileAttack,

+ 10 - 2
mifare_fuzzer/mifare_fuzzer_i.h

@@ -3,9 +3,14 @@
 #include <furi.h>
 #include <furi_hal.h>
 
+#include <lib/nfc/nfc_device.h>
+#include <lib/nfc/protocols/nfc_protocol.h>
+#include "lib/nfc/protocols/mf_classic/mf_classic.h"
+
 #include <gui/gui.h>
 #include <gui/view_dispatcher.h>
 #include <gui/scene_manager.h>
+#include <notification/notification_messages.h>
 
 #include <gui/modules/submenu.h>
 
@@ -28,7 +33,8 @@
 #define TAG "MifareFuzzerApp"
 
 #define MIFARE_FUZZER_APP_FOLDER EXT_PATH("mifare_fuzzer")
-#define MIFARE_FUZZER_FILE_EXT ".txt"
+#define MIFARE_FUZZER_UID_FILE_EXT ".txt"
+#define MIFARE_FUZZER_CARD_FILE_EXT ".nfc"
 
 #define MIFARE_FUZZER_TICK_PERIOD 200
 #define MIFARE_FUZZER_DEFAULT_TICKS_BETWEEN_CARDS 10
@@ -49,6 +55,7 @@ typedef enum {
 
 struct MifareFuzzerApp {
     Gui* gui;
+    NotificationApp* notifications;
 
     ViewDispatcher* view_dispatcher;
 
@@ -69,7 +76,8 @@ struct MifareFuzzerApp {
     MifareCard card;
     MifareFuzzerAttack attack;
     FuriString* app_folder;
-    FuriString* file_path;
+    FuriString* card_file_path;
+    FuriString* uid_file_path;
     FuriString* uid_str;
     Stream* uids_stream;
 };

+ 42 - 7
mifare_fuzzer/mifare_fuzzer_worker.c

@@ -1,4 +1,5 @@
 #include "mifare_fuzzer_worker.h"
+#include "mifare_fuzzer_i.h"
 
 /// @brief mifare_fuzzer_worker_alloc()
 /// @return
@@ -50,23 +51,31 @@ void mifare_fuzzer_worker_start(MifareFuzzerWorker* mifare_fuzzer_worker) {
 /// @return
 int32_t mifare_fuzzer_worker_task(void* context) {
     MifareFuzzerWorker* mifare_fuzzer_worker = context;
+    FURI_LOG_D(TAG, "mifare_fuzzer_worker_task()");
 
     if(mifare_fuzzer_worker->state == MifareFuzzerWorkerStateEmulate) {
-        const Iso14443_3aData* data =
-            nfc_device_get_data(mifare_fuzzer_worker->nfc_device, NfcProtocolIso14443_3a);
-
-        mifare_fuzzer_worker->nfc_listener =
-            nfc_listener_alloc(mifare_fuzzer_worker->nfc, NfcProtocolIso14443_3a, data);
+        NfcDevice* device = mifare_fuzzer_worker->nfc_device;
+        NfcProtocol protocol = nfc_device_get_protocol(device);
+
+        FURI_LOG_D(
+            TAG,
+            "mifare_fuzzer_worker_task() :: Allocating and starting with %s",
+            nfc_device_get_protocol_name(protocol));
+        mifare_fuzzer_worker->nfc_listener = nfc_listener_alloc(
+            mifare_fuzzer_worker->nfc, protocol, nfc_device_get_data(device, protocol));
         nfc_listener_start(mifare_fuzzer_worker->nfc_listener, NULL, NULL);
 
+        FURI_LOG_D(TAG, "mifare_fuzzer_worker_task() :: Emulating...");
         while(mifare_fuzzer_worker->state == MifareFuzzerWorkerStateEmulate) {
             furi_delay_ms(50);
         }
 
+        FURI_LOG_D(TAG, "mifare_fuzzer_worker_task() :: Stopping NFC Listener");
         nfc_listener_stop(mifare_fuzzer_worker->nfc_listener);
         nfc_listener_free(mifare_fuzzer_worker->nfc_listener);
     }
 
+    FURI_LOG_D(TAG, "mifare_fuzzer_worker_task() :: Stopping");
     mifare_fuzzer_worker->state = MifareFuzzerWorkerStateStop;
 
     return 0;
@@ -82,6 +91,26 @@ bool mifare_fuzzer_worker_is_emulating(MifareFuzzerWorker* mifare_fuzzer_worker)
     return false;
 }
 
+/// @brief mifare_fuzzer_worker_set_nfc_device()
+/// @param mifare_fuzzer_worker
+/// @param nfc_device
+void mifare_fuzzer_worker_set_nfc_device(
+    MifareFuzzerWorker* mifare_fuzzer_worker,
+    NfcDevice* nfc_device) {
+    FURI_LOG_D(
+        TAG,
+        "mifare_fuzzer_worker_set_nfc_device() :: Protocol: %s",
+        nfc_device_get_protocol_name(nfc_device_get_protocol(nfc_device)));
+    mifare_fuzzer_worker->nfc_device = nfc_device;
+}
+
+/// @brief mifare_fuzzer_worker_get_nfc_device()
+/// @param mifare_fuzzer_worker
+/// @return
+NfcDevice* mifare_fuzzer_worker_get_nfc_device(MifareFuzzerWorker* mifare_fuzzer_worker) {
+    return mifare_fuzzer_worker->nfc_device;
+}
+
 /// @brief mifare_fuzzer_worker_set_nfc_data()
 /// @param mifare_fuzzer_worker
 /// @param nfc_data
@@ -94,9 +123,15 @@ void mifare_fuzzer_worker_set_nfc_data(
     memcpy(nfc_14a_data->atqa, nfc_data.atqa, ATQA_LEN);
     nfc_14a_data->sak = nfc_data.sak;
 
+    FURI_LOG_D(
+        TAG,
+        "mifare_fuzzer_worker_set_nfc_data() :: Clearing nfc_device and setting Iso14443_3aData");
     nfc_device_clear(mifare_fuzzer_worker->nfc_device);
     nfc_device_set_data(mifare_fuzzer_worker->nfc_device, NfcProtocolIso14443_3a, nfc_14a_data);
-
+    FURI_LOG_D(
+        TAG,
+        "mifare_fuzzer_worker_set_nfc_data() :: Protocol: %s",
+        nfc_device_get_protocol_name(nfc_device_get_protocol(mifare_fuzzer_worker->nfc_device)));
     iso14443_3a_free(nfc_14a_data);
 }
 
@@ -105,4 +140,4 @@ void mifare_fuzzer_worker_set_nfc_data(
 /// @return
 Iso14443_3aData mifare_fuzzer_worker_get_nfc_data(MifareFuzzerWorker* mifare_fuzzer_worker) {
     return mifare_fuzzer_worker->nfc_data;
-}
+}

+ 9 - 2
mifare_fuzzer/mifare_fuzzer_worker.h

@@ -5,6 +5,7 @@
 #include <nfc/nfc_device.h>
 #include <nfc/nfc_listener.h>
 #include <nfc/protocols/iso14443_3a/iso14443_3a.h>
+#include <nfc/protocols/iso14443_3a/iso14443_3a_listener.h>
 
 typedef enum MifareFuzzerWorkerState {
     MifareFuzzerWorkerStateEmulate,
@@ -17,9 +18,9 @@ typedef enum MifareFuzzerWorkerState {
 typedef struct MifareFuzzerWorker {
     FuriThread* thread;
     MifareFuzzerWorkerState state;
-    Iso14443_3aData nfc_data;
     NfcListener* nfc_listener;
     NfcDevice* nfc_device;
+    Iso14443_3aData nfc_data;
     Nfc* nfc;
 } MifareFuzzerWorker;
 
@@ -32,7 +33,13 @@ void mifare_fuzzer_worker_start(MifareFuzzerWorker* mifare_fuzzer_worker);
 int32_t mifare_fuzzer_worker_task(void* context);
 //
 bool mifare_fuzzer_worker_is_emulating(MifareFuzzerWorker* mifare_fuzzer_worker);
+
+void mifare_fuzzer_worker_set_nfc_device(
+    MifareFuzzerWorker* mifare_fuzzer_worker,
+    NfcDevice* nfc_device);
+NfcDevice* mifare_fuzzer_worker_get_nfc_device(MifareFuzzerWorker* mifare_fuzzer_worker);
+
 void mifare_fuzzer_worker_set_nfc_data(
     MifareFuzzerWorker* mifare_fuzzer_worker,
     Iso14443_3aData nfc_data);
-Iso14443_3aData mifare_fuzzer_worker_get_nfc_data(MifareFuzzerWorker* mifare_fuzzer_worker);
+Iso14443_3aData mifare_fuzzer_worker_get_nfc_data(MifareFuzzerWorker* mifare_fuzzer_worker);

+ 4 - 3
mifare_fuzzer/scenes/mifare_fuzzer_scene_attack.c

@@ -106,15 +106,16 @@ bool mifare_fuzzer_scene_attack_on_event(void* context, SceneManagerEvent event)
             mifare_fuzzer_emulator_set_attack(app->emulator_view, app->attack);
             // open dialog file
             DialogsFileBrowserOptions browser_options;
-            dialog_file_browser_set_basic_options(&browser_options, MIFARE_FUZZER_FILE_EXT, NULL);
+            dialog_file_browser_set_basic_options(
+                &browser_options, MIFARE_FUZZER_UID_FILE_EXT, NULL);
             browser_options.hide_ext = false;
             bool res = dialog_file_browser_show(
-                app->dialogs, app->file_path, app->app_folder, &browser_options);
+                app->dialogs, app->uid_file_path, app->app_folder, &browser_options);
             if(res) {
                 app->uids_stream = buffered_file_stream_alloc(app->storage);
                 res = buffered_file_stream_open(
                     app->uids_stream,
-                    furi_string_get_cstr(app->file_path),
+                    furi_string_get_cstr(app->uid_file_path),
                     FSAM_READ,
                     FSOM_OPEN_EXISTING);
                 if(res) {

+ 61 - 28
mifare_fuzzer/scenes/mifare_fuzzer_scene_emulator.c

@@ -1,4 +1,6 @@
 #include "../mifare_fuzzer_i.h"
+#include <notification/notification.h>
+#include <notification/notification_messages.h>
 
 uint8_t tick_counter = 0;
 uint8_t attack_step = 0;
@@ -69,16 +71,38 @@ void mifare_fuzzer_scene_emulator_on_enter(void* context) {
 /// @return
 bool mifare_fuzzer_scene_emulator_on_event(void* context, SceneManagerEvent event) {
     //FURI_LOG_D(TAG, "mifare_fuzzer_scene_emulator_on_event()");
-    Iso14443_3aData nfc_data;
-
     MifareFuzzerApp* app = context;
     MifareFuzzerEmulator* emulator = app->emulator_view;
-
     bool consumed = false;
 
     if(event.type == SceneManagerEventTypeCustom) {
         if(event.event == MifareFuzzerEventStartAttack) {
-            //FURI_LOG_D(TAG, "mifare_fuzzer_scene_emulator_on_event() :: MifareFuzzerEventStartAttack");
+            NfcDevice* nfc_device = NULL;
+            const MfClassicData* mf_classic_data = NULL;
+            bool nfc_device_parsed = false;
+            if(app->card_file_path) {
+                nfc_device = app->worker->nfc_device;
+                const char* path = furi_string_get_cstr(app->card_file_path);
+                if(nfc_device_load(nfc_device, path)) {
+                    nfc_device_parsed = true;
+                    mf_classic_data = nfc_device_get_data(nfc_device, NfcProtocolMfClassic);
+                    if(mf_classic_data->type == MfClassicType1k) {
+                        app->card = MifareCardClassic1k;
+                    } else if(mf_classic_data->type == MfClassicType4k) {
+                        app->card = MifareCardClassic4k;
+                    } else if(nfc_device_get_protocol(nfc_device) == NfcProtocolMfUltralight) {
+                        app->card = MifareCardUltralight;
+                    }
+                    mifare_fuzzer_emulator_set_card(emulator, app->card, app->card_file_path);
+                }
+            }
+
+            Iso14443_3aData* nfc_data;
+            if(mf_classic_data) {
+                nfc_data = mf_classic_data->iso14443_3a_data;
+            } else {
+                nfc_data = iso14443_3a_alloc();
+            }
 
             // Stop worker
             mifare_fuzzer_worker_stop(app->worker);
@@ -86,27 +110,27 @@ bool mifare_fuzzer_scene_emulator_on_event(void* context, SceneManagerEvent even
             // Set card type
             // TODO: Move somewhere else, I do not like this to be there
             if(app->card == MifareCardClassic1k) {
-                nfc_data.atqa[0] = 0x04;
-                nfc_data.atqa[1] = 0x00;
-                nfc_data.sak = 0x08;
-                nfc_data.uid_len = 0x04;
+                nfc_data->atqa[0] = 0x04;
+                nfc_data->atqa[1] = 0x00;
+                nfc_data->sak = 0x08;
+                nfc_data->uid_len = 0x04;
             } else if(app->card == MifareCardClassic4k) {
-                nfc_data.atqa[0] = 0x02;
-                nfc_data.atqa[1] = 0x00;
-                nfc_data.sak = 0x18;
-                nfc_data.uid_len = 0x04;
+                nfc_data->atqa[0] = 0x02;
+                nfc_data->atqa[1] = 0x00;
+                nfc_data->sak = 0x18;
+                nfc_data->uid_len = 0x04;
             } else if(app->card == MifareCardUltralight) {
-                nfc_data.atqa[0] = 0x44;
-                nfc_data.atqa[1] = 0x00;
-                nfc_data.sak = 0x00;
-                nfc_data.uid_len = 0x07;
+                nfc_data->atqa[0] = 0x44;
+                nfc_data->atqa[1] = 0x00;
+                nfc_data->sak = 0x00;
+                nfc_data->uid_len = 0x07;
             }
 
             // Set UIDs
             if(app->attack == MifareFuzzerAttackTestValues) {
                 // Load test UIDs
-                for(uint8_t i = 0; i < nfc_data.uid_len; i++) {
-                    nfc_data.uid[i] = id_uid_test[attack_step][i];
+                for(uint8_t i = 0; i < nfc_data->uid_len; i++) {
+                    nfc_data->uid[i] = id_uid_test[attack_step][i];
                 }
                 // Next UIDs on next loop
                 if(attack_step >= 8) {
@@ -123,13 +147,13 @@ bool mifare_fuzzer_scene_emulator_on_event(void* context, SceneManagerEvent even
 
                     // TODO: Manufacture-code must be selectable from a list
                     // use a fixed manufacture-code for now: 0x04 = NXP Semiconductors Germany
-                    nfc_data.uid[0] = 0x04;
-                    for(uint8_t i = 1; i < nfc_data.uid_len; i++) {
-                        nfc_data.uid[i] = (furi_hal_random_get() & 0xFF);
+                    nfc_data->uid[0] = 0x04;
+                    for(uint8_t i = 1; i < nfc_data->uid_len; i++) {
+                        nfc_data->uid[i] = (furi_hal_random_get() & 0xFF);
                     }
                 } else {
-                    for(uint8_t i = 0; i < nfc_data.uid_len; i++) {
-                        nfc_data.uid[i] = (furi_hal_random_get() & 0xFF);
+                    for(uint8_t i = 0; i < nfc_data->uid_len; i++) {
+                        nfc_data->uid[i] = (furi_hal_random_get() & 0xFF);
                     }
                 }
             } else if(app->attack == MifareFuzzerAttackLoadUidsFromFile) {
@@ -157,21 +181,25 @@ bool mifare_fuzzer_scene_emulator_on_event(void* context, SceneManagerEvent even
 
                 // parse string to UID
                 // TODO: a better validation on input?
-                for(uint8_t i = 0; i < nfc_data.uid_len; i++) {
+                for(uint8_t i = 0; i < nfc_data->uid_len; i++) {
                     if(i <= ((furi_string_size(app->uid_str) - 1) / 2)) {
                         char temp_str[3];
                         temp_str[0] = furi_string_get_cstr(app->uid_str)[i * 2];
                         temp_str[1] = furi_string_get_cstr(app->uid_str)[i * 2 + 1];
                         temp_str[2] = '\0';
-                        nfc_data.uid[i] = (uint8_t)strtol(temp_str, NULL, 16);
+                        nfc_data->uid[i] = (uint8_t)strtol(temp_str, NULL, 16);
                     } else {
-                        nfc_data.uid[i] = 0x00;
+                        nfc_data->uid[i] = 0x00;
                     }
                 }
             }
 
-            mifare_fuzzer_worker_set_nfc_data(app->worker, nfc_data);
-            mifare_fuzzer_emulator_set_nfc_data(app->emulator_view, nfc_data);
+            mifare_fuzzer_emulator_set_nfc_data(app->emulator_view, *nfc_data);
+            if(nfc_device_parsed) {
+                mifare_fuzzer_worker_set_nfc_device(app->worker, nfc_device);
+            } else {
+                mifare_fuzzer_worker_set_nfc_data(app->worker, *nfc_data);
+            }
 
             // Reset tick_counter
             tick_counter = 0;
@@ -180,10 +208,14 @@ bool mifare_fuzzer_scene_emulator_on_event(void* context, SceneManagerEvent even
             // Start worker
             mifare_fuzzer_worker_start(app->worker);
 
+            if(nfc_device_parsed) {
+                notification_message(app->notifications, &sequence_blink_start_magenta);
+            }
         } else if(event.event == MifareFuzzerEventStopAttack) {
             //FURI_LOG_D(TAG, "mifare_fuzzer_scene_emulator_on_event() :: MifareFuzzerEventStopAttack");
             // Stop worker
             mifare_fuzzer_worker_stop(app->worker);
+            notification_message(app->notifications, &sequence_blink_stop);
         } else if(event.event == MifareFuzzerEventIncrementTicks) {
             if(!emulator->is_attacking) {
                 if(emulator->ticks_between_cards < MIFARE_FUZZER_MAX_TICKS_BETWEEN_CARDS) {
@@ -234,6 +266,7 @@ bool mifare_fuzzer_scene_emulator_on_event(void* context, SceneManagerEvent even
 void mifare_fuzzer_scene_emulator_on_exit(void* context) {
     //FURI_LOG_D(TAG, "mifare_fuzzer_scene_emulator_on_exit()");
     MifareFuzzerApp* app = context;
+    notification_message(app->notifications, &sequence_blink_stop);
     mifare_fuzzer_worker_stop(app->worker);
 
     if(app->attack == MifareFuzzerAttackLoadUidsFromFile) {

+ 36 - 3
mifare_fuzzer/scenes/mifare_fuzzer_scene_start.c

@@ -5,6 +5,7 @@ enum SubmenuIndex {
     SubmenuIndexClassic1k,
     SubmenuIndexClassic4k,
     SubmenuIndexUltralight,
+    SubmenuIndexFile,
 };
 
 /// @brief mifare_fuzzer_scene_start_submenu_callback()
@@ -24,6 +25,9 @@ void mifare_fuzzer_scene_start_submenu_callback(void* context, uint32_t index) {
     case SubmenuIndexUltralight:
         custom_event = MifareFuzzerEventUltralight;
         break;
+    case SubmenuIndexFile:
+        custom_event = MifareFuzzerEventFile;
+        break;
     default:
         return;
     }
@@ -57,6 +61,12 @@ void mifare_fuzzer_scene_start_on_enter(void* context) {
         SubmenuIndexUltralight,
         mifare_fuzzer_scene_start_submenu_callback,
         app);
+    submenu_add_item(
+        submenu_card,
+        "From file",
+        SubmenuIndexFile,
+        mifare_fuzzer_scene_start_submenu_callback,
+        app);
 
     // set selected menu
     submenu_set_selected_item(
@@ -69,6 +79,9 @@ void mifare_fuzzer_scene_start_on_enter(void* context) {
 /// @param context
 /// @param event
 /// @return
+/// @param context
+/// @param event
+/// @return
 bool mifare_fuzzer_scene_start_on_event(void* context, SceneManagerEvent event) {
     //FURI_LOG_D(TAG, "mifare_fuzzer_scene_start_on_event()");
     MifareFuzzerApp* app = context;
@@ -76,13 +89,14 @@ bool mifare_fuzzer_scene_start_on_event(void* context, SceneManagerEvent event)
 
     if(event.type == SceneManagerEventTypeCustom) {
         //FURI_LOG_D(TAG, "mifare_fuzzer_scene_start_on_event() :: event.event = %ld", event.event);
+        app->card_file_path = NULL;
         if(event.event == MifareFuzzerEventClassic1k) {
             // save selected item
             scene_manager_set_scene_state(
                 app->scene_manager, MifareFuzzerSceneStart, SubmenuIndexClassic1k);
             // set emulator card
             app->card = MifareCardClassic1k;
-            mifare_fuzzer_emulator_set_card(app->emulator_view, app->card);
+            mifare_fuzzer_emulator_set_card(app->emulator_view, app->card, NULL);
             // open next scene
             scene_manager_next_scene(app->scene_manager, MifareFuzzerSceneAttack);
             consumed = true;
@@ -92,7 +106,7 @@ bool mifare_fuzzer_scene_start_on_event(void* context, SceneManagerEvent event)
                 app->scene_manager, MifareFuzzerSceneStart, SubmenuIndexClassic4k);
             // set emulator card
             app->card = MifareCardClassic4k;
-            mifare_fuzzer_emulator_set_card(app->emulator_view, app->card);
+            mifare_fuzzer_emulator_set_card(app->emulator_view, app->card, NULL);
             // open next scene
             scene_manager_next_scene(app->scene_manager, MifareFuzzerSceneAttack);
             consumed = true;
@@ -102,10 +116,29 @@ bool mifare_fuzzer_scene_start_on_event(void* context, SceneManagerEvent event)
                 app->scene_manager, MifareFuzzerSceneStart, SubmenuIndexUltralight);
             // set emulator card
             app->card = MifareCardUltralight;
-            mifare_fuzzer_emulator_set_card(app->emulator_view, app->card);
+            mifare_fuzzer_emulator_set_card(app->emulator_view, app->card, NULL);
             // open next scene
             scene_manager_next_scene(app->scene_manager, MifareFuzzerSceneAttack);
             consumed = true;
+        } else if(event.event == MifareFuzzerEventFile) {
+            // save selected item
+            scene_manager_set_scene_state(
+                app->scene_manager, MifareFuzzerSceneStart, SubmenuIndexFile);
+            // Don't set emulator card (yet), read file instead
+            DialogsFileBrowserOptions browser_options;
+            FuriString* initial_path = furi_string_alloc_set("/ext/nfc");
+            dialog_file_browser_set_basic_options(
+                &browser_options, MIFARE_FUZZER_CARD_FILE_EXT, NULL);
+            browser_options.hide_ext = false;
+            app->card_file_path = furi_string_alloc();
+            bool wasFileSelected = dialog_file_browser_show(
+                app->dialogs, app->card_file_path, initial_path, &browser_options);
+            furi_string_free(initial_path);
+            if(wasFileSelected) {
+                // open next scene
+                scene_manager_next_scene(app->scene_manager, MifareFuzzerSceneAttack);
+                consumed = true;
+            }
         }
     } else if(event.type == SceneManagerEventTypeTick) {
         //FURI_LOG_D(TAG, "mifare_fuzzer_scene_start_on_event() :: SceneManagerEventTypeTick");

+ 21 - 13
mifare_fuzzer/views/mifare_fuzzer_emulator.c

@@ -60,8 +60,8 @@ static void mifare_fuzzer_emulator_draw_callback(Canvas* canvas, void* _model) {
     canvas_draw_str(canvas, 15, 22, model->mifare_card_dsc);
     // Timing
     furi_string_printf(furi_string, "%d", model->ticks_between_cards);
-    canvas_draw_str(canvas, 90, 22, "t:");
-    canvas_draw_str(canvas, 100, 22, furi_string_get_cstr(furi_string));
+    canvas_draw_str(canvas, 100, 33, "t:");
+    canvas_draw_str(canvas, 110, 33, furi_string_get_cstr(furi_string));
     // Attack
     canvas_draw_str(canvas, 4, 33, "a:");
     canvas_draw_str(canvas, 15, 33, model->attack_dsc);
@@ -202,7 +202,8 @@ View* mifare_fuzzer_emulator_get_view(MifareFuzzerEmulator* mifare_fuzzer_emulat
 /// @param mifare_card
 void mifare_fuzzer_emulator_set_card(
     MifareFuzzerEmulator* mifare_fuzzer_emulator,
-    MifareCard mifare_card) {
+    MifareCard mifare_card,
+    FuriString* name) {
     furi_assert(mifare_fuzzer_emulator);
     furi_assert(mifare_card);
 
@@ -211,16 +212,23 @@ void mifare_fuzzer_emulator_set_card(
         MifareFuzzerEmulatorModel * model,
         {
             model->mifare_card = mifare_card;
-            switch(mifare_card) {
-            case MifareCardClassic1k:
-                model->mifare_card_dsc = "Classic 1k";
-                break;
-            case MifareCardClassic4k:
-                model->mifare_card_dsc = "Classic 4k";
-                break;
-            case MifareCardUltralight:
-                model->mifare_card_dsc = "Ultralight";
-                break;
+            if(name == NULL) {
+                switch(mifare_card) {
+                case MifareCardClassic1k:
+                    model->mifare_card_dsc = "Classic 1k";
+                    break;
+                case MifareCardClassic4k:
+                    model->mifare_card_dsc = "Classic 4k";
+                    break;
+                case MifareCardUltralight:
+                    model->mifare_card_dsc = "Ultralight";
+                    break;
+                }
+            } else {
+                FuriString* card_name = furi_string_alloc_set(name);
+                size_t filename_start = furi_string_search_rchar(card_name, '/') + 1;
+                furi_string_right(card_name, filename_start);
+                model->mifare_card_dsc = furi_string_get_cstr(card_name);
             }
         },
         true);

+ 3 - 1
mifare_fuzzer/views/mifare_fuzzer_emulator.h

@@ -4,6 +4,7 @@
 #include <furi_hal.h>
 #include <gui/view.h>
 #include <gui/elements.h>
+#include <lib/nfc/nfc_device.h>
 
 #include "lib/nfc/protocols/iso14443_3a/iso14443_3a.h"
 
@@ -49,7 +50,8 @@ View* mifare_fuzzer_emulator_get_view(MifareFuzzerEmulator* context);
 
 void mifare_fuzzer_emulator_set_card(
     MifareFuzzerEmulator* mifare_fuzzer_emulator,
-    MifareCard mifare_card);
+    MifareCard mifare_card,
+    FuriString* name);
 void mifare_fuzzer_emulator_set_attack(
     MifareFuzzerEmulator* mifare_fuzzer_emulator,
     MifareFuzzerAttack mifare_attack);