From f07048d1b095c39d978fb1e43056c2b511839e64 Mon Sep 17 00:00:00 2001 From: RebornedBrain Date: Mon, 7 Apr 2025 00:49:22 +0300 Subject: [PATCH] [FL-3902] NFC app now can launch MFKey32 (#4117) * feat: app chaining * add `launch_current_app_after_deferred`, remove `get_referring_application` * fix naming * new api * fix f18 * Added new function which enqueues external app from nfc app * Added path to MFKey32 app * Button "Crack nonces in MFKey32" added to ReadMenu scene * SaveConfirm scene adjusted to move to different scenes depending on state * SaveSuccess scene now moves to different scenes depending on SaveConfirm scene state * MfKeyComplete scene shows different text when MFKey.fap present on the device * Now MfKeyClassic scene can run external app * fix deferred launches after errors Co-authored-by: Anna Antonenko Co-authored-by: Aleksandr Kutuzov Co-authored-by: hedger --- .../protocol_support/mf_classic/mf_classic.c | 20 +++++- applications/main/nfc/nfc_app.c | 20 ++++++ applications/main/nfc/nfc_app_i.h | 10 +++ .../nfc_scene_mf_classic_mfkey_complete.c | 71 ++++++++++++++----- .../main/nfc/scenes/nfc_scene_save_confirm.c | 9 ++- .../main/nfc/scenes/nfc_scene_save_success.c | 8 ++- 6 files changed, 118 insertions(+), 20 deletions(-) diff --git a/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c b/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c index 6f7be7f4c..5c668d530 100644 --- a/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c +++ b/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c @@ -14,7 +14,8 @@ enum { SubmenuIndexDetectReader = SubmenuIndexCommonMax, SubmenuIndexWrite, SubmenuIndexUpdate, - SubmenuIndexDictAttack + SubmenuIndexDictAttack, + SubmenuIndexCrackNonces, }; static void nfc_scene_info_on_enter_mf_classic(NfcApp* instance) { @@ -128,6 +129,13 @@ static void nfc_scene_read_menu_on_enter_mf_classic(NfcApp* instance) { SubmenuIndexDictAttack, nfc_protocol_support_common_submenu_callback, instance); + + submenu_add_item( + submenu, + "Crack nonces in MFKey32", + SubmenuIndexCrackNonces, + nfc_protocol_support_common_submenu_callback, + instance); } } @@ -193,6 +201,11 @@ static bool nfc_scene_read_menu_on_event_mf_classic(NfcApp* instance, SceneManag if(event.type == SceneManagerEventTypeCustom) { if(event.event == SubmenuIndexDetectReader) { + scene_manager_set_scene_state( + instance->scene_manager, + NfcSceneSaveConfirm, + NfcSceneSaveConfirmStateDetectReader); + scene_manager_next_scene(instance->scene_manager, NfcSceneSaveConfirm); dolphin_deed(DolphinDeedNfcDetectReader); consumed = true; @@ -205,6 +218,11 @@ static bool nfc_scene_read_menu_on_event_mf_classic(NfcApp* instance, SceneManag } else if(event.event == SubmenuIndexCommonEdit) { scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid); consumed = true; + } else if(event.event == SubmenuIndexCrackNonces) { + scene_manager_set_scene_state( + instance->scene_manager, NfcSceneSaveConfirm, NfcSceneSaveConfirmStateCrackNonces); + scene_manager_next_scene(instance->scene_manager, NfcSceneSaveConfirm); + consumed = true; } } diff --git a/applications/main/nfc/nfc_app.c b/applications/main/nfc/nfc_app.c index 5365d6bef..7a829c329 100644 --- a/applications/main/nfc/nfc_app.c +++ b/applications/main/nfc/nfc_app.c @@ -470,6 +470,26 @@ static void nfc_show_initial_scene_for_device(NfcApp* nfc) { scene_manager_next_scene(nfc->scene_manager, scene); } +void nfc_app_run_external(NfcApp* nfc, const char* app_path) { + furi_assert(nfc); + furi_assert(app_path); + + Loader* loader = furi_record_open(RECORD_LOADER); + + loader_enqueue_launch(loader, app_path, NULL, LoaderDeferredLaunchFlagGui); + + FuriString* self_path = furi_string_alloc(); + furi_check(loader_get_application_launch_path(loader, self_path)); + + loader_enqueue_launch( + loader, furi_string_get_cstr(self_path), NULL, LoaderDeferredLaunchFlagGui); + furi_string_free(self_path); + + furi_record_close(RECORD_LOADER); + + view_dispatcher_stop(nfc->view_dispatcher); +} + int32_t nfc_app(void* p) { if(!nfc_is_hal_ready()) return 0; diff --git a/applications/main/nfc/nfc_app_i.h b/applications/main/nfc/nfc_app_i.h index 1101b7bb2..920127fef 100644 --- a/applications/main/nfc/nfc_app_i.h +++ b/applications/main/nfc/nfc_app_i.h @@ -35,6 +35,7 @@ #include "helpers/felica_auth.h" #include "helpers/slix_unlock.h" +#include #include #include #include @@ -80,6 +81,8 @@ #define NFC_APP_MF_CLASSIC_DICT_SYSTEM_NESTED_PATH \ (NFC_APP_FOLDER "/assets/mf_classic_dict_nested.nfc") +#define NFC_MFKEY32_APP_PATH (EXT_PATH("apps/NFC/mfkey.fap")) + typedef enum { NfcRpcStateIdle, NfcRpcStateEmulating, @@ -167,6 +170,11 @@ typedef enum { NfcViewDetectReader, } NfcView; +typedef enum { + NfcSceneSaveConfirmStateDetectReader, + NfcSceneSaveConfirmStateCrackNonces, +} NfcSceneSaveConfirmState; + int32_t nfc_task(void* p); void nfc_text_store_set(NfcApp* nfc, const char* text, ...); @@ -202,3 +210,5 @@ bool nfc_save_file(NfcApp* instance, FuriString* path); void nfc_make_app_folder(NfcApp* instance); void nfc_append_filename_string_when_present(NfcApp* instance, FuriString* string); + +void nfc_app_run_external(NfcApp* nfc, const char* app_path); diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_mfkey_complete.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_mfkey_complete.c index 660674ceb..3311ef0e0 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_mfkey_complete.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_mfkey_complete.c @@ -1,4 +1,10 @@ #include "../nfc_app_i.h" +#include "loader/loader.h" + +typedef enum { + NfcSceneMfClassicMfKeyCompleteStateAppMissing, + NfcSceneMfClassicMfKeyCompleteStateAppPresent, +} NfcSceneMfClassicMfKeyCompleteState; void nfc_scene_mf_classic_mfkey_complete_callback( GuiButtonType result, @@ -15,22 +21,47 @@ void nfc_scene_mf_classic_mfkey_complete_on_enter(void* context) { widget_add_string_element( instance->widget, 64, 0, AlignCenter, AlignTop, FontPrimary, "Completed!"); - widget_add_string_multiline_element( - instance->widget, - 64, - 13, - AlignCenter, - AlignTop, - FontSecondary, - "Now use Mfkey32 to extract \nkeys: r.flipper.net/nfc-tools"); - widget_add_icon_element(instance->widget, 50, 39, &I_MFKey_qr_25x25); - widget_add_button_element( - instance->widget, - GuiButtonTypeRight, - "Finish", - nfc_scene_mf_classic_mfkey_complete_callback, - instance); + NfcSceneMfClassicMfKeyCompleteState scene_state = + storage_common_exists(instance->storage, NFC_MFKEY32_APP_PATH) ? + NfcSceneMfClassicMfKeyCompleteStateAppPresent : + NfcSceneMfClassicMfKeyCompleteStateAppMissing; + scene_manager_set_scene_state( + instance->scene_manager, NfcSceneMfClassicMfkeyComplete, scene_state); + + if(scene_state == NfcSceneMfClassicMfKeyCompleteStateAppMissing) { + widget_add_string_multiline_element( + instance->widget, + 64, + 13, + AlignCenter, + AlignTop, + FontSecondary, + "Now use Mfkey32 to extract \nkeys: r.flipper.net/nfc-tools"); + widget_add_icon_element(instance->widget, 50, 39, &I_MFKey_qr_25x25); + widget_add_button_element( + instance->widget, + GuiButtonTypeRight, + "Finish", + nfc_scene_mf_classic_mfkey_complete_callback, + instance); + } else { + widget_add_string_multiline_element( + instance->widget, + 60, + 16, + AlignLeft, + AlignTop, + FontSecondary, + "Now run Mfkey32\n to extract \nkeys"); + widget_add_icon_element(instance->widget, 5, 18, &I_WarningDolphin_45x42); + widget_add_button_element( + instance->widget, + GuiButtonTypeRight, + "Run", + nfc_scene_mf_classic_mfkey_complete_callback, + instance); + } view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewWidget); } @@ -40,8 +71,14 @@ bool nfc_scene_mf_classic_mfkey_complete_on_event(void* context, SceneManagerEve if(event.type == SceneManagerEventTypeCustom) { if(event.event == GuiButtonTypeRight) { - consumed = scene_manager_search_and_switch_to_previous_scene( - instance->scene_manager, NfcSceneStart); + NfcSceneMfClassicMfKeyCompleteState scene_state = scene_manager_get_scene_state( + instance->scene_manager, NfcSceneMfClassicMfkeyComplete); + if(scene_state == NfcSceneMfClassicMfKeyCompleteStateAppMissing) { + consumed = scene_manager_search_and_switch_to_previous_scene( + instance->scene_manager, NfcSceneStart); + } else { + nfc_app_run_external(instance, NFC_MFKEY32_APP_PATH); + } } } else if(event.type == SceneManagerEventTypeBack) { const uint32_t prev_scenes[] = {NfcSceneSavedMenu, NfcSceneStart}; diff --git a/applications/main/nfc/scenes/nfc_scene_save_confirm.c b/applications/main/nfc/scenes/nfc_scene_save_confirm.c index 9d0a206d3..d8fb20642 100644 --- a/applications/main/nfc/scenes/nfc_scene_save_confirm.c +++ b/applications/main/nfc/scenes/nfc_scene_save_confirm.c @@ -29,7 +29,14 @@ bool nfc_scene_save_confirm_on_event(void* context, SceneManagerEvent event) { scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); consumed = true; } else if(event.event == DialogExResultLeft) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicDetectReader); + NfcSceneSaveConfirmState scene_state = + scene_manager_get_scene_state(nfc->scene_manager, NfcSceneSaveConfirm); + + NfcScene scene = scene_state == NfcSceneSaveConfirmStateCrackNonces ? + NfcSceneMfClassicMfkeyComplete : + NfcSceneMfClassicDetectReader; + + scene_manager_next_scene(nfc->scene_manager, scene); consumed = true; } } diff --git a/applications/main/nfc/scenes/nfc_scene_save_success.c b/applications/main/nfc/scenes/nfc_scene_save_success.c index 1c76b3f87..5f812ba9c 100644 --- a/applications/main/nfc/scenes/nfc_scene_save_success.c +++ b/applications/main/nfc/scenes/nfc_scene_save_success.c @@ -29,7 +29,13 @@ bool nfc_scene_save_success_on_event(void* context, SceneManagerEvent event) { consumed = scene_manager_search_and_switch_to_previous_scene( nfc->scene_manager, NfcSceneMfClassicKeys); } else if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSaveConfirm)) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicDetectReader); + NfcSceneSaveConfirmState scene_state = + scene_manager_get_scene_state(nfc->scene_manager, NfcSceneSaveConfirm); + + NfcScene scene = scene_state == NfcSceneSaveConfirmStateCrackNonces ? + NfcSceneMfClassicMfkeyComplete : + NfcSceneMfClassicDetectReader; + scene_manager_next_scene(nfc->scene_manager, scene); consumed = true; } else if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) { consumed = scene_manager_search_and_switch_to_another_scene(