tcpassos 2 лет назад
Родитель
Сommit
343a22a7b9
16 измененных файлов с 1518 добавлено и 89 удалено
  1. 1 0
      applications/external/wifi_marauder_companion/application.fam
  2. BIN
      applications/external/wifi_marauder_companion/icons/DolphinCommon_56x48.png
  3. 7 0
      applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_config.h
  4. 0 2
      applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_console_output.c
  5. 57 0
      applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_confirm_delete.c
  6. 112 0
      applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_edit.c
  7. 148 0
      applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_edit_list.c
  8. 65 0
      applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_options.c
  9. 24 34
      applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_select.c
  10. 186 0
      applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_stage_add.c
  11. 687 0
      applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_stage_edit.c
  12. 108 0
      applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_user_input.c
  13. 15 28
      applications/external/wifi_marauder_companion/script/wifi_marauder_script.c
  14. 26 18
      applications/external/wifi_marauder_companion/script/wifi_marauder_script.h
  15. 43 5
      applications/external/wifi_marauder_companion/wifi_marauder_app.c
  16. 39 2
      applications/external/wifi_marauder_companion/wifi_marauder_app_i.h

+ 1 - 0
applications/external/wifi_marauder_companion/application.fam

@@ -8,5 +8,6 @@ App(
     stack_size=4 * 1024,
     order=90,
     fap_icon="wifi_10px.png",
+    fap_icon_assets="icons",
     fap_category="GPIO",
 )

BIN
applications/external/wifi_marauder_companion/icons/DolphinCommon_56x48.png


+ 7 - 0
applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_config.h

@@ -3,4 +3,11 @@ ADD_SCENE(wifi_marauder, console_output, ConsoleOutput)
 ADD_SCENE(wifi_marauder, text_input, TextInput)
 ADD_SCENE(wifi_marauder, settings_init, SettingsInit)
 ADD_SCENE(wifi_marauder, log_viewer, LogViewer)
+ADD_SCENE(wifi_marauder, user_input, UserInput)
 ADD_SCENE(wifi_marauder, script_select, ScriptSelect)
+ADD_SCENE(wifi_marauder, script_options, ScriptOptions)
+ADD_SCENE(wifi_marauder, script_edit, ScriptEdit)
+ADD_SCENE(wifi_marauder, script_confirm_delete, ScriptConfirmDelete)
+ADD_SCENE(wifi_marauder, script_stage_edit, ScriptStageEdit)
+ADD_SCENE(wifi_marauder, script_stage_add, ScriptStageAdd)
+ADD_SCENE(wifi_marauder, script_stage_edit_list, ScriptStageEditList)

+ 0 - 2
applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_console_output.c

@@ -175,8 +175,6 @@ void wifi_marauder_scene_console_output_on_exit(void* context) {
 
     wifi_marauder_script_worker_free(app->script_worker);
     app->script_worker = NULL;
-    wifi_marauder_script_free(app->script);
-    app->script = NULL;
 
     app->is_writing_pcap = false;
     if(app->capture_file && storage_file_is_open(app->capture_file)) {

+ 57 - 0
applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_confirm_delete.c

@@ -0,0 +1,57 @@
+#include "../wifi_marauder_app_i.h"
+
+void wifi_marauder_scene_script_confirm_delete_widget_callback(
+    GuiButtonType result,
+    InputType type,
+    void* context) {
+    WifiMarauderApp* app = context;
+    if(type == InputTypeShort) {
+        view_dispatcher_send_custom_event(app->view_dispatcher, result);
+    }
+}
+
+void wifi_marauder_scene_script_confirm_delete_on_enter(void* context) {
+    WifiMarauderApp* app = context;
+    Widget* widget = app->script_confirm_delete_widget;
+
+    widget_add_button_element(widget, GuiButtonTypeLeft, "No", wifi_marauder_scene_script_confirm_delete_widget_callback, app);
+    widget_add_button_element(widget, GuiButtonTypeRight, "Yes", wifi_marauder_scene_script_confirm_delete_widget_callback, app);
+
+    widget_add_string_element(widget, 0, 0, AlignLeft, AlignTop, FontPrimary, "Are you sure?");
+    widget_add_text_box_element(
+        widget,
+        0, 12, 128, 38,
+        AlignCenter, AlignCenter,
+        "The script will be\npermanently deleted",
+        false);
+
+    view_dispatcher_switch_to_view(app->view_dispatcher, WifiMarauderAppViewScriptConfirmDelete);
+}
+
+bool wifi_marauder_scene_script_confirm_delete_on_event(void* context, SceneManagerEvent event) {
+    WifiMarauderApp* app = context;
+    bool consumed = false;
+
+    if(event.type == SceneManagerEventTypeCustom) {
+        // get which button press: "Yes" or "No"
+        if(event.event == GuiButtonTypeRight) {
+            // Yes
+            if (app->script != NULL) {
+                char script_path[256];
+                snprintf(script_path, sizeof(script_path), "%s/%s.json", MARAUDER_APP_FOLDER_SCRIPTS, app->script->name);
+                storage_simply_remove(app->storage, script_path);
+                wifi_marauder_script_free(app->script);
+                app->script = NULL;
+            }
+        }
+        scene_manager_previous_scene(app->scene_manager);
+        consumed = true;
+    }
+
+    return consumed;
+}
+
+void wifi_marauder_scene_script_confirm_delete_on_exit(void* context) {
+    WifiMarauderApp* app = context;
+    widget_reset(app->script_confirm_delete_widget);
+}

+ 112 - 0
applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_edit.c

@@ -0,0 +1,112 @@
+#include "../wifi_marauder_app_i.h"
+
+static void wifi_marauder_scene_script_edit_callback(void* context, uint32_t index) {
+    WifiMarauderApp* app = context;
+    WifiMarauderScriptStage* current_stage = app->script->first_stage;
+    uint32_t stage_index = 0;
+
+    while (current_stage != NULL && stage_index < index) {
+        current_stage = current_stage->next_stage;
+        stage_index++;
+    }
+    app->script_edit_selected_stage = current_stage;
+
+    if (app->script_edit_selected_stage != NULL) {
+        scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneScriptEdit, index);
+        scene_manager_next_scene(app->scene_manager, WifiMarauderSceneScriptStageEdit);
+    }
+}
+
+static void wifi_marauder_scene_script_edit_add_callback(void* context, uint32_t index) {
+    WifiMarauderApp* app = context;
+    scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneScriptEdit, index);
+    scene_manager_next_scene(app->scene_manager, WifiMarauderSceneScriptStageAdd);
+}
+
+static void wifi_marauder_scene_script_edit_save_callback(void* context, uint32_t index) {
+    UNUSED(index);
+    WifiMarauderApp* app = context;
+    
+    char script_path[256];
+    snprintf(script_path, sizeof(script_path), "%s/%s.json", MARAUDER_APP_FOLDER_SCRIPTS, app->script->name);
+    wifi_marauder_script_save_json(app->storage, script_path, app->script);
+
+    DialogMessage* message = dialog_message_alloc();
+    dialog_message_set_text(message, "Saved!", 88, 32, AlignCenter, AlignCenter);
+    dialog_message_set_icon(message, &I_DolphinCommon_56x48, 5, 6);
+    dialog_message_set_buttons(message, NULL, "Ok", NULL);
+    dialog_message_show(app->dialogs, message);
+    dialog_message_free(message);
+}
+
+void wifi_marauder_scene_script_edit_on_enter(void* context) {
+    WifiMarauderApp* app = context;
+    Submenu* script_edit_submenu = app->script_edit_submenu;
+    WifiMarauderScript* script = app->script;
+    submenu_set_header(script_edit_submenu, script->name);
+
+    WifiMarauderScriptStage* current_stage = script->first_stage;
+    int stage_index = 0;
+    while (current_stage != NULL) {
+        switch (current_stage->type) {
+            case WifiMarauderScriptStageTypeScan:
+                submenu_add_item(script_edit_submenu, "Scan", stage_index, wifi_marauder_scene_script_edit_callback, app);
+                break;
+            case WifiMarauderScriptStageTypeSelect:
+                submenu_add_item(script_edit_submenu, "Select", stage_index, wifi_marauder_scene_script_edit_callback, app);
+                break;
+            case WifiMarauderScriptStageTypeDeauth:
+                submenu_add_item(script_edit_submenu, "Deauth", stage_index, wifi_marauder_scene_script_edit_callback, app);
+                break;
+            case WifiMarauderScriptStageTypeProbe:
+                submenu_add_item(script_edit_submenu, "Probe", stage_index, wifi_marauder_scene_script_edit_callback, app);
+                break;
+            case WifiMarauderScriptStageTypeSniffRaw:
+                submenu_add_item(script_edit_submenu, "Sniff raw", stage_index, wifi_marauder_scene_script_edit_callback, app);
+                break;
+            case WifiMarauderScriptStageTypeSniffBeacon:
+                submenu_add_item(script_edit_submenu, "Sniff beacon", stage_index, wifi_marauder_scene_script_edit_callback, app);
+                break;
+            case WifiMarauderScriptStageTypeSniffDeauth:
+                submenu_add_item(script_edit_submenu, "Sniff deauth", stage_index, wifi_marauder_scene_script_edit_callback, app);
+                break;
+            case WifiMarauderScriptStageTypeSniffEsp:
+                submenu_add_item(script_edit_submenu, "Sniff esp", stage_index, wifi_marauder_scene_script_edit_callback, app);
+                break;
+            case WifiMarauderScriptStageTypeSniffPmkid:
+                submenu_add_item(script_edit_submenu, "Sniff PMKID", stage_index, wifi_marauder_scene_script_edit_callback, app);
+                break;
+            case WifiMarauderScriptStageTypeSniffPwn:
+                submenu_add_item(script_edit_submenu, "Sniff pwn", stage_index, wifi_marauder_scene_script_edit_callback, app);
+                break;
+            case WifiMarauderScriptStageTypeBeaconList:
+                submenu_add_item(script_edit_submenu, "Beacon list", stage_index, wifi_marauder_scene_script_edit_callback, app);
+                break;
+            case WifiMarauderScriptStageTypeBeaconAp:
+                submenu_add_item(script_edit_submenu, "Beacon AP", stage_index, wifi_marauder_scene_script_edit_callback, app);
+                break;
+            default:
+                submenu_add_item(script_edit_submenu, "Unknown", stage_index, wifi_marauder_scene_script_edit_callback, app);
+                break;
+        }
+        current_stage = current_stage->next_stage;
+        stage_index++;
+    }
+
+    submenu_add_item(script_edit_submenu, "[+] ADD STAGE", stage_index++, wifi_marauder_scene_script_edit_add_callback, app);
+    submenu_add_item(script_edit_submenu, "[*] SAVE", stage_index, wifi_marauder_scene_script_edit_save_callback, app);
+
+    submenu_set_selected_item(script_edit_submenu, scene_manager_get_scene_state(app->scene_manager, WifiMarauderSceneScriptEdit));
+    view_dispatcher_switch_to_view(app->view_dispatcher, WifiMarauderAppViewScriptEdit);
+}
+
+bool wifi_marauder_scene_script_edit_on_event(void* context, SceneManagerEvent event) {
+    UNUSED(context);
+    UNUSED(event);
+    return false;
+}
+
+void wifi_marauder_scene_script_edit_on_exit(void* context) {
+    WifiMarauderApp* app = context;
+    submenu_reset(app->script_edit_submenu);
+}

+ 148 - 0
applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_edit_list.c

@@ -0,0 +1,148 @@
+#include "../wifi_marauder_app_i.h"
+
+static void wifi_marauder_scene_script_stage_edit_list_add_callback(void* context, uint32_t index) {
+    WifiMarauderApp* app = context;
+
+    // Creates new item
+    WifiMarauderScriptStageListItem* new_item = (WifiMarauderScriptStageListItem*) malloc(sizeof(WifiMarauderScriptStageListItem));
+    new_item->value = malloc(64);
+    new_item->next_item = NULL;
+    
+    if (app->script_stage_edit_first_item == NULL) {
+        app->script_stage_edit_first_item = new_item;
+    } else {
+        WifiMarauderScriptStageListItem* last_item = app->script_stage_edit_first_item;
+        while (last_item->next_item != NULL) {
+            last_item = last_item->next_item;
+        }
+        last_item->next_item = new_item;
+    }
+
+    scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneScriptStageEditList, index);
+    app->user_input_type = WifiMarauderUserInputTypeString;
+    app->user_input_string_reference = &new_item->value;
+    scene_manager_next_scene(app->scene_manager, WifiMarauderSceneUserInput);
+}
+
+static void wifi_marauder_scene_script_stage_edit_list_deallocate_items(WifiMarauderApp* app) {
+    WifiMarauderScriptStageListItem* current_item = app->script_stage_edit_first_item;
+    while (current_item != NULL) {
+        WifiMarauderScriptStageListItem* next_item = current_item->next_item;
+        free(current_item->value);
+        free(current_item);
+        current_item = next_item;
+    }
+    app->script_stage_edit_first_item = NULL;
+}
+
+static void wifi_marauder_scene_script_stage_edit_list_save_strings(WifiMarauderApp* app) {
+    WifiMarauderScriptStageListItem* current_item = app->script_stage_edit_first_item;
+    int array_size = 0;
+
+    // Calculates the required array size
+    while (current_item != NULL) {
+        array_size++;
+        current_item = current_item->next_item;
+    }
+
+    // Reallocate the array of strings if necessary
+    if (*app->script_stage_edit_string_count_reference < array_size) {
+        *app->script_stage_edit_strings_reference = realloc(*app->script_stage_edit_strings_reference, array_size);
+    }
+
+    // Fills the array of strings
+    current_item = app->script_stage_edit_first_item;
+    int i = 0;
+    while (current_item != NULL) {
+        strncpy((*app->script_stage_edit_strings_reference)[i], current_item->value, strlen(current_item->value) + 1);
+        current_item = current_item->next_item;
+        i++;
+    }
+
+    *app->script_stage_edit_string_count_reference = array_size;
+}
+
+static void wifi_marauder_scene_script_stage_edit_list_save_numbers(WifiMarauderApp* app) {
+    WifiMarauderScriptStageListItem* current_item = app->script_stage_edit_first_item;
+    int array_size = 0;
+
+    // Calculates the required array size
+    while (current_item != NULL) {
+        array_size++;
+        current_item = current_item->next_item;
+    }
+
+    // Reallocate the array of integers if necessary
+    if (*app->script_stage_edit_number_count_reference < array_size) {
+        *app->script_stage_edit_numbers_reference = realloc(*app->script_stage_edit_numbers_reference, array_size * sizeof(int));
+    }
+
+    // Fills the array of integers
+    current_item = app->script_stage_edit_first_item;
+    int i = 0;
+    while (current_item != NULL) {
+        (*app->script_stage_edit_numbers_reference)[i] = atoi(current_item->value);
+        current_item = current_item->next_item;
+        i++;
+    }
+
+    *app->script_stage_edit_number_count_reference = array_size;
+}
+
+static void wifi_marauder_scene_script_stage_edit_list_save_callback(void* context, uint32_t index) {
+    UNUSED(index);
+    WifiMarauderApp* app = context;
+
+    if (app->script_stage_edit_strings_reference != NULL && app->script_stage_edit_string_count_reference != NULL) {
+        wifi_marauder_scene_script_stage_edit_list_save_strings(app);
+    }
+
+    if (app->script_stage_edit_numbers_reference != NULL && app->script_stage_edit_number_count_reference != NULL) {
+        wifi_marauder_scene_script_stage_edit_list_save_numbers(app);
+    }
+
+    wifi_marauder_scene_script_stage_edit_list_deallocate_items(app);
+    scene_manager_previous_scene(app->scene_manager);
+}
+
+static void wifi_marauder_scene_script_stage_edit_list_clear_callback(void* context, uint32_t index) {
+    UNUSED(index);
+    WifiMarauderApp* app = context;
+
+    wifi_marauder_scene_script_stage_edit_list_deallocate_items(app);
+
+    submenu_reset(app->script_stage_edit_list_submenu);
+    submenu_add_item(app->script_stage_edit_list_submenu, "[+] ADD ITEM", 99, wifi_marauder_scene_script_stage_edit_list_add_callback, app);
+    submenu_add_item(app->script_stage_edit_list_submenu, "[*] SAVE ITEMS", 99, wifi_marauder_scene_script_stage_edit_list_save_callback, app);
+    submenu_add_item(app->script_stage_edit_list_submenu, "[-] CLEAR LIST", 99, wifi_marauder_scene_script_stage_edit_list_clear_callback, app);
+}
+
+void wifi_marauder_scene_script_stage_edit_list_on_enter(void* context) {
+    WifiMarauderApp* app = context;
+    Submenu* script_stage_edit_list_submenu = app->script_stage_edit_list_submenu;
+
+    int item_index = 0;
+    WifiMarauderScriptStageListItem* current_item = app->script_stage_edit_first_item;
+
+    while (current_item != NULL) {
+        submenu_add_item(script_stage_edit_list_submenu, current_item->value, item_index++, NULL, app);
+        current_item = current_item->next_item;
+    }
+    submenu_add_item(app->script_stage_edit_list_submenu, "[+] ADD ITEM", 99, wifi_marauder_scene_script_stage_edit_list_add_callback, app);
+    submenu_add_item(app->script_stage_edit_list_submenu, "[*] SAVE ITEMS", 99, wifi_marauder_scene_script_stage_edit_list_save_callback, app);
+    submenu_add_item(app->script_stage_edit_list_submenu, "[-] CLEAR LIST", 99, wifi_marauder_scene_script_stage_edit_list_clear_callback, app);
+
+    submenu_set_selected_item(script_stage_edit_list_submenu, scene_manager_get_scene_state(app->scene_manager, WifiMarauderSceneScriptStageEditList));
+    view_dispatcher_switch_to_view(app->view_dispatcher, WifiMarauderAppViewScriptStageEditList);
+}
+
+bool wifi_marauder_scene_script_stage_edit_list_on_event(void* context, SceneManagerEvent event) {
+    UNUSED(context);
+    UNUSED(event);
+    return false;
+}
+
+void wifi_marauder_scene_script_stage_edit_list_on_exit(void* context) {
+    WifiMarauderApp* app = context;
+    submenu_reset(app->script_stage_edit_list_submenu);
+}

+ 65 - 0
applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_options.c

@@ -0,0 +1,65 @@
+#include "../wifi_marauder_app_i.h"
+
+enum SubmenuIndex {
+    SubmenuIndexRun,
+    SubmenuIndexEdit,
+    SubmenuIndexDelete
+};
+
+static void wifi_marauder_scene_script_options_callback(void* context, uint32_t index) {
+    WifiMarauderApp* app = context;
+
+    switch (index) {
+        case SubmenuIndexRun:
+            scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneScriptOptions, index);
+            scene_manager_next_scene(app->scene_manager, WifiMarauderSceneConsoleOutput);
+            break;
+        case SubmenuIndexEdit:
+            scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneScriptOptions, index);
+            scene_manager_next_scene(app->scene_manager, WifiMarauderSceneScriptEdit);
+            break;
+        case SubmenuIndexDelete:
+            scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneScriptOptions, index);
+            scene_manager_next_scene(app->scene_manager, WifiMarauderSceneScriptConfirmDelete);
+            break;
+        default:
+            break;
+    }
+}
+
+void wifi_marauder_scene_script_options_on_enter(void* context) {
+    WifiMarauderApp* app = context;
+
+    // If returning after confirming script deletion
+    if (app->script == NULL) {
+        scene_manager_previous_scene(app->scene_manager);
+        return;
+    }
+
+    Submenu* script_options_submenu = app->script_options_submenu;
+
+    submenu_set_header(script_options_submenu, app->script->name);
+    submenu_add_item(script_options_submenu, "[>] RUN", SubmenuIndexRun, wifi_marauder_scene_script_options_callback, app);
+    submenu_add_item(script_options_submenu, "[*] EDIT", SubmenuIndexEdit, wifi_marauder_scene_script_options_callback, app);
+    submenu_add_item(script_options_submenu, "[X] DELETE", SubmenuIndexDelete, wifi_marauder_scene_script_options_callback, app);
+
+    submenu_set_selected_item(script_options_submenu, scene_manager_get_scene_state(app->scene_manager, WifiMarauderSceneScriptOptions));
+    view_dispatcher_switch_to_view(app->view_dispatcher, WifiMarauderAppViewScriptOptions);
+}
+
+bool wifi_marauder_scene_script_options_on_event(void* context, SceneManagerEvent event) {
+    WifiMarauderApp* app = context;
+    bool consumed = false;
+
+    if(event.type == SceneManagerEventTypeBack) {
+        wifi_marauder_script_free(app->script);
+        app->script = NULL;
+    }
+
+    return consumed;
+}
+
+void wifi_marauder_scene_script_options_on_exit(void* context) {
+    WifiMarauderApp* app = context;
+    submenu_reset(app->script_options_submenu);
+}

+ 24 - 34
applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_select.c

@@ -1,26 +1,31 @@
 #include "../wifi_marauder_app_i.h"
 
-static void wifi_marauder_scene_script_select_script_list_enter_callback(void* context, uint32_t index) {
-    furi_assert(context);
+static void wifi_marauder_scene_script_select_callback(void* context, uint32_t index) {
     WifiMarauderApp* app = context;
-    if (app->script_list_count == 0) {
-        return;
-    }
 
     char script_path[256];
     snprintf(script_path, sizeof(script_path), "%s/%s.json", MARAUDER_APP_FOLDER_SCRIPTS, furi_string_get_cstr(app->script_list[index]));
 
     app->script = wifi_marauder_script_parse_json(app->storage, script_path);
     if (app->script) {
-        scene_manager_next_scene(app->scene_manager, WifiMarauderSceneConsoleOutput);
+        scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneScriptSelect, index);
+        scene_manager_next_scene(app->scene_manager, WifiMarauderSceneScriptOptions);
     }
 }
 
+static void wifi_marauder_scene_script_select_add_callback(void* context, uint32_t index) {
+    WifiMarauderApp* app = context;
+    scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneScriptSelect, index);
+
+    app->user_input_type = WifiMarauderUserInputTypeFileName;
+    app->user_input_file_dir = strdup(MARAUDER_APP_FOLDER_SCRIPTS);
+    app->user_input_file_extension = strdup("json");
+    scene_manager_next_scene(app->scene_manager, WifiMarauderSceneUserInput);
+}
+
 void wifi_marauder_scene_script_select_on_enter(void* context) {
-    furi_assert(context);
     WifiMarauderApp* app = context;
-    VariableItemList* script_var_item_list = app->script_var_item_list;
-    variable_item_list_set_enter_callback(script_var_item_list, wifi_marauder_scene_script_select_script_list_enter_callback, app);
+    Submenu* script_select_submenu = app->script_select_submenu;
 
     File* dir_scripts = storage_file_alloc(app->storage);
     if(storage_dir_open(dir_scripts, MARAUDER_APP_FOLDER_SCRIPTS)) {
@@ -32,6 +37,7 @@ void wifi_marauder_scene_script_select_on_enter(void* context) {
             app->script_list_count++;
         }
         if (app->script_list_count > 0) {
+            submenu_set_header(script_select_submenu, "Select a script:");
             app->script_list = malloc(app->script_list_count * sizeof(FuriString*));
             storage_dir_close(dir_scripts);
             storage_dir_open(dir_scripts, MARAUDER_APP_FOLDER_SCRIPTS);
@@ -40,49 +46,33 @@ void wifi_marauder_scene_script_select_on_enter(void* context) {
             while(storage_dir_read(dir_scripts, &file_info, file_path, 255)) {
                 app->script_list[script_index] = furi_string_alloc();
                 path_extract_filename_no_ext(file_path, app->script_list[script_index]);
-                variable_item_list_add(script_var_item_list, furi_string_get_cstr(app->script_list[script_index]), 1, NULL, app);
+                submenu_add_item(script_select_submenu, furi_string_get_cstr(app->script_list[script_index]), script_index, wifi_marauder_scene_script_select_callback, app);
                 script_index++;
             }
         } else {
-            variable_item_list_add(script_var_item_list, "No script found", 1, NULL, app);
+            submenu_set_header(script_select_submenu, "No script found");
         }
+        submenu_add_item(script_select_submenu, "[+] ADD SCRIPT", 99, wifi_marauder_scene_script_select_add_callback, app);
         storage_dir_close(dir_scripts);
     }
     storage_file_free(dir_scripts);
 
-    variable_item_list_set_selected_item(script_var_item_list, scene_manager_get_scene_state(app->scene_manager, WifiMarauderSceneScriptSelect));
+    submenu_set_selected_item(script_select_submenu, scene_manager_get_scene_state(app->scene_manager, WifiMarauderSceneScriptSelect));
     view_dispatcher_switch_to_view(app->view_dispatcher, WifiMarauderAppViewScriptSelect);
 }
 
 bool wifi_marauder_scene_script_select_on_event(void* context, SceneManagerEvent event) {
-    furi_assert(context);
-    WifiMarauderApp* app = context;
-    bool consumed = false;
-
-    if(event.type == SceneManagerEventTypeCustom) {
-        if(event.event == WifiMarauderEventStartConsole) {
-            scene_manager_set_scene_state(
-                app->scene_manager, WifiMarauderSceneScriptSelect, app->selected_script_index);
-            scene_manager_next_scene(app->scene_manager, WifiMarauderSceneConsoleOutput);
-        }
-        consumed = true;
-    } else if(event.type == SceneManagerEventTypeTick) {
-        app->selected_script_index = variable_item_list_get_selected_item_index(app->var_item_list);
-        consumed = true;
-    }
-
-    return consumed;
+    UNUSED(context);
+    UNUSED(event);
+    return false;
 }
 
 void wifi_marauder_scene_script_select_on_exit(void* context) {
-    furi_assert(context);
     WifiMarauderApp* app = context;
-
-    variable_item_list_reset(app->script_var_item_list);
+    submenu_reset(app->script_select_submenu);
 
     for (int i = 0; i < app->script_list_count; i++) {
         furi_string_free(app->script_list[i]);
     }
-    free(app->script_list);
-    
+    free(app->script_list);   
 }

+ 186 - 0
applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_stage_add.c

@@ -0,0 +1,186 @@
+#include "../wifi_marauder_app_i.h"
+
+// Scan
+static void wifi_marauder_scene_script_stage_add_scan_callback(void* context, uint32_t index) {
+    UNUSED(index);
+    WifiMarauderApp* app = context;
+
+    WifiMarauderScriptStageScan* stage = (WifiMarauderScriptStageScan*) malloc(sizeof(WifiMarauderScriptStageScan));
+    stage->type = WifiMarauderScriptScanTypeAp;
+    stage->timeout = WIFI_MARAUDER_DEFAULT_TIMEOUT_SCAN;
+
+    wifi_marauder_script_add_stage(app->script, WifiMarauderScriptStageTypeScan, stage);
+    scene_manager_previous_scene(app->scene_manager);
+}
+
+// Select
+static void wifi_marauder_scene_script_stage_add_select_callback(void* context, uint32_t index) {
+    UNUSED(index);
+    WifiMarauderApp* app = context;
+
+    WifiMarauderScriptStageSelect* stage = (WifiMarauderScriptStageSelect*) malloc(sizeof(WifiMarauderScriptStageSelect));
+    stage->type = WifiMarauderScriptSelectTypeAp;
+    stage->filter = strdup("all");
+    
+    wifi_marauder_script_add_stage(app->script, WifiMarauderScriptStageTypeSelect, stage);
+    scene_manager_previous_scene(app->scene_manager);
+}
+
+// Deauth
+static void wifi_marauder_scene_script_stage_add_deauth_callback(void* context, uint32_t index) {
+    UNUSED(index);
+    WifiMarauderApp* app = context;
+
+    WifiMarauderScriptStageDeauth* stage = (WifiMarauderScriptStageDeauth*) malloc(sizeof(WifiMarauderScriptStageDeauth));
+    stage->timeout = WIFI_MARAUDER_DEFAULT_TIMEOUT_DEAUTH;
+    
+    wifi_marauder_script_add_stage(app->script, WifiMarauderScriptStageTypeDeauth, stage);
+    scene_manager_previous_scene(app->scene_manager);
+}
+
+// Probe
+static void wifi_marauder_scene_script_stage_add_probe_callback(void* context, uint32_t index) {
+    UNUSED(index);
+    WifiMarauderApp* app = context;
+
+    WifiMarauderScriptStageProbe* stage = (WifiMarauderScriptStageProbe*) malloc(sizeof(WifiMarauderScriptStageProbe));
+    stage->timeout = WIFI_MARAUDER_DEFAULT_TIMEOUT_PROBE;
+    
+    wifi_marauder_script_add_stage(app->script, WifiMarauderScriptStageTypeProbe, stage);
+    scene_manager_previous_scene(app->scene_manager);
+}
+
+// Sniff RAW
+static void wifi_marauder_scene_script_stage_add_sniffraw_callback(void* context, uint32_t index) {
+    UNUSED(index);
+    WifiMarauderApp* app = context;
+
+    WifiMarauderScriptStageSniffRaw* stage = (WifiMarauderScriptStageSniffRaw*) malloc(sizeof(WifiMarauderScriptStageSniffRaw));
+    stage->timeout = WIFI_MARAUDER_DEFAULT_TIMEOUT_SNIFF;
+    
+    wifi_marauder_script_add_stage(app->script, WifiMarauderScriptStageTypeSniffRaw, stage);
+    scene_manager_previous_scene(app->scene_manager);
+}
+
+// Sniff Beacon
+static void wifi_marauder_scene_script_stage_add_sniffbeacon_callback(void* context, uint32_t index) {
+    UNUSED(index);
+    WifiMarauderApp* app = context;
+
+    WifiMarauderScriptStageSniffBeacon* stage = (WifiMarauderScriptStageSniffBeacon*) malloc(sizeof(WifiMarauderScriptStageSniffBeacon));
+    stage->timeout = WIFI_MARAUDER_DEFAULT_TIMEOUT_SNIFF;
+    
+    wifi_marauder_script_add_stage(app->script, WifiMarauderScriptStageTypeSniffBeacon, stage);
+    scene_manager_previous_scene(app->scene_manager);
+}
+
+// Sniff Deauth
+static void wifi_marauder_scene_script_stage_add_sniffdeauth_callback(void* context, uint32_t index) {
+    UNUSED(index);
+    WifiMarauderApp* app = context;
+
+    WifiMarauderScriptStageSniffDeauth* stage = (WifiMarauderScriptStageSniffDeauth*) malloc(sizeof(WifiMarauderScriptStageSniffDeauth));
+    stage->timeout = WIFI_MARAUDER_DEFAULT_TIMEOUT_SNIFF;
+    
+    wifi_marauder_script_add_stage(app->script, WifiMarauderScriptStageTypeSniffDeauth, stage);
+    scene_manager_previous_scene(app->scene_manager);
+}
+
+// Sniff Esp
+static void wifi_marauder_scene_script_stage_add_sniffesp_callback(void* context, uint32_t index) {
+    UNUSED(index);
+    WifiMarauderApp* app = context;
+
+    WifiMarauderScriptStageSniffEsp* stage = (WifiMarauderScriptStageSniffEsp*) malloc(sizeof(WifiMarauderScriptStageSniffEsp));
+    stage->timeout = WIFI_MARAUDER_DEFAULT_TIMEOUT_SNIFF;
+    
+    wifi_marauder_script_add_stage(app->script, WifiMarauderScriptStageTypeSniffEsp, stage);
+    scene_manager_previous_scene(app->scene_manager);
+}
+
+// Sniff PMKID
+static void wifi_marauder_scene_script_stage_add_sniffpmkid_callback(void* context, uint32_t index) {
+    UNUSED(index);
+    WifiMarauderApp* app = context;
+
+    WifiMarauderScriptStageSniffPmkid* stage = (WifiMarauderScriptStageSniffPmkid*) malloc(sizeof(WifiMarauderScriptStageSniffPmkid));
+    stage->channel = 0;
+    stage->force_deauth = WifiMarauderScriptBooleanTrue;
+    stage->timeout = WIFI_MARAUDER_DEFAULT_TIMEOUT_SNIFF;
+    
+    wifi_marauder_script_add_stage(app->script, WifiMarauderScriptStageTypeSniffPmkid, stage);
+    scene_manager_previous_scene(app->scene_manager);
+}
+
+// Sniff Pwn
+static void wifi_marauder_scene_script_stage_add_sniffpwn_callback(void* context, uint32_t index) {
+    UNUSED(index);
+    WifiMarauderApp* app = context;
+
+    WifiMarauderScriptStageSniffPwn* stage = (WifiMarauderScriptStageSniffPwn*) malloc(sizeof(WifiMarauderScriptStageSniffPwn));
+    stage->timeout = WIFI_MARAUDER_DEFAULT_TIMEOUT_SNIFF;
+    
+    wifi_marauder_script_add_stage(app->script, WifiMarauderScriptStageTypeSniffPwn, stage);
+    scene_manager_previous_scene(app->scene_manager);
+}
+
+// Beacon list
+static void wifi_marauder_scene_script_stage_add_beaconlist_callback(void* context, uint32_t index) {
+    UNUSED(index);
+    WifiMarauderApp* app = context;
+
+    WifiMarauderScriptStageBeaconList* stage = (WifiMarauderScriptStageBeaconList*) malloc(sizeof(WifiMarauderScriptStageBeaconList));
+    stage->ssids = NULL;
+    stage->ssid_count = 0;
+    stage->random_ssids = 0;
+    stage->timeout = WIFI_MARAUDER_DEFAULT_TIMEOUT_BEACON;
+
+    wifi_marauder_script_add_stage(app->script, WifiMarauderScriptStageTypeBeaconList, stage);
+    scene_manager_previous_scene(app->scene_manager);
+}
+
+// Beacon AP
+static void wifi_marauder_scene_script_stage_add_beaconap_callback(void* context, uint32_t index) {
+    UNUSED(index);
+    WifiMarauderApp* app = context;
+
+    WifiMarauderScriptStageBeaconAp* stage = (WifiMarauderScriptStageBeaconAp*) malloc(sizeof(WifiMarauderScriptStageBeaconAp));
+    stage->timeout = WIFI_MARAUDER_DEFAULT_TIMEOUT_BEACON;
+
+    wifi_marauder_script_add_stage(app->script, WifiMarauderScriptStageTypeBeaconAp, stage);
+    scene_manager_previous_scene(app->scene_manager);
+}
+
+void wifi_marauder_scene_script_stage_add_on_enter(void* context) {
+    WifiMarauderApp* app = context;
+    Submenu* script_stage_add_submenu = app->script_stage_add_submenu;
+    submenu_set_header(script_stage_add_submenu, "Add stage");
+
+    int menu_index = 0;
+    submenu_add_item(script_stage_add_submenu, "Scan", menu_index++, wifi_marauder_scene_script_stage_add_scan_callback, app);
+    submenu_add_item(script_stage_add_submenu, "Select", menu_index++, wifi_marauder_scene_script_stage_add_select_callback, app);
+    submenu_add_item(script_stage_add_submenu, "Deauth", menu_index++, wifi_marauder_scene_script_stage_add_deauth_callback, app);
+    submenu_add_item(script_stage_add_submenu, "Probe", menu_index++, wifi_marauder_scene_script_stage_add_probe_callback, app);
+    submenu_add_item(script_stage_add_submenu, "Sniff RAW", menu_index++, wifi_marauder_scene_script_stage_add_sniffraw_callback, app);
+    submenu_add_item(script_stage_add_submenu, "Sniff Beacon", menu_index++, wifi_marauder_scene_script_stage_add_sniffbeacon_callback, app);
+    submenu_add_item(script_stage_add_submenu, "Sniff Deauth", menu_index++, wifi_marauder_scene_script_stage_add_sniffdeauth_callback, app);
+    submenu_add_item(script_stage_add_submenu, "Sniff Esp", menu_index++, wifi_marauder_scene_script_stage_add_sniffesp_callback, app);
+    submenu_add_item(script_stage_add_submenu, "Sniff PMKID", menu_index++, wifi_marauder_scene_script_stage_add_sniffpmkid_callback, app);
+    submenu_add_item(script_stage_add_submenu, "Sniff Pwnagotchi", menu_index++, wifi_marauder_scene_script_stage_add_sniffpwn_callback, app);
+    submenu_add_item(script_stage_add_submenu, "Beacon List", menu_index++, wifi_marauder_scene_script_stage_add_beaconlist_callback, app);
+    submenu_add_item(script_stage_add_submenu, "Beacon AP", menu_index++, wifi_marauder_scene_script_stage_add_beaconap_callback, app);
+
+    submenu_set_selected_item(script_stage_add_submenu, scene_manager_get_scene_state(app->scene_manager, WifiMarauderSceneScriptEdit));
+    view_dispatcher_switch_to_view(app->view_dispatcher, WifiMarauderAppViewScriptStageAdd);
+}
+
+bool wifi_marauder_scene_script_stage_add_on_event(void* context, SceneManagerEvent event) {
+    UNUSED(context);
+    UNUSED(event);
+    return false;
+}
+
+void wifi_marauder_scene_script_stage_add_on_exit(void* context) {
+    WifiMarauderApp* app = context;
+    submenu_reset(app->script_stage_add_submenu);
+}

+ 687 - 0
applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_stage_edit.c

@@ -0,0 +1,687 @@
+#include "../wifi_marauder_app_i.h"
+
+typedef void (*VariableItemSetupCallback)(VariableItem* item);
+typedef void (*VariableItemSelectCallback)(WifiMarauderApp* app);
+
+typedef enum WifiMarauderScriptEditItemType {
+    WifiMarauderScriptEditItemTypeString,
+    WifiMarauderScriptEditItemTypeNumber,
+    WifiMarauderScriptEditItemTypeOptionsString,
+    WifiMarauderScriptEditItemTypeOptionsNumber,
+    WifiMarauderScriptEditItemTypeListString,
+    WifiMarauderScriptEditItemTypeListNumber
+} WifiMarauderScriptEditItemType;
+
+
+#define MAX_OPTIONS (12)
+typedef struct WifiMarauderScriptEditItem {
+    char* name;
+    WifiMarauderScriptEditItemType type;
+    int num_options;
+    char* options[MAX_OPTIONS];
+    VariableItemSetupCallback setup_callback;
+    VariableItemChangeCallback change_callback;
+    VariableItemSelectCallback select_callback;
+} WifiMarauderScriptEditItem;
+
+// Menu items =============================================================================================================================
+WifiMarauderScriptEditItem* stage_items = NULL;
+uint32_t num_stage_items = 0;
+
+void wifi_marauder_scene_script_stage_edit_create_list_strings(WifiMarauderApp* app, char** strings, int string_count) {
+    // Deallocates the existing list
+    WifiMarauderScriptStageListItem* current_item = app->script_stage_edit_first_item;
+    while (current_item != NULL) {
+        WifiMarauderScriptStageListItem* next_item = current_item->next_item;
+        free(current_item->value);
+        free(current_item);
+        current_item = next_item;
+    }
+
+    // Create a new list with numbers
+    WifiMarauderScriptStageListItem* first_item = NULL;
+    WifiMarauderScriptStageListItem* previous_item = NULL;
+    for (int i = 0; i < string_count; i++) {        
+        WifiMarauderScriptStageListItem* item = malloc(sizeof(WifiMarauderScriptStageListItem));
+        item->value = strdup(strings[i]);
+        item->next_item = NULL;
+
+        if (previous_item == NULL) {
+            first_item = item;
+        } else {
+            previous_item->next_item = item;
+        }
+        previous_item = item;
+    }
+
+    app->script_stage_edit_first_item = first_item;
+}
+
+void wifi_marauder_scene_script_stage_edit_create_list_numbers(WifiMarauderApp* app, int* numbers, int number_count) {
+    // Deallocates the existing list
+    WifiMarauderScriptStageListItem* current_item = app->script_stage_edit_first_item;
+    while (current_item != NULL) {
+        WifiMarauderScriptStageListItem* next_item = current_item->next_item;
+        free(current_item->value);
+        free(current_item);
+        current_item = next_item;
+    }
+
+    // Create a new list with numbers
+    WifiMarauderScriptStageListItem* first_item = NULL;
+    WifiMarauderScriptStageListItem* previous_item = NULL;
+    for (int i = 0; i < number_count; i++) {
+        char number_str[32];
+        snprintf(number_str, sizeof(number_str), "%d", numbers[i]);
+        
+        WifiMarauderScriptStageListItem* item = malloc(sizeof(WifiMarauderScriptStageListItem));
+        item->value = strdup(number_str);
+        item->next_item = NULL;
+
+        if (previous_item == NULL) {
+            first_item = item;
+        } else {
+            previous_item->next_item = item;
+        }
+        previous_item = item;
+    }
+
+    app->script_stage_edit_first_item = first_item;
+}
+
+static void wifi_marauder_scene_script_stage_edit_list_enter_callback(void* context, uint32_t index) {
+    WifiMarauderApp* app = context;
+    const WifiMarauderScriptEditItem* menu_item = &stage_items[index];
+
+    // Fixed delete item
+    if (index == num_stage_items) {
+        uint32_t deleted_stage_index = scene_manager_get_scene_state(app->scene_manager, WifiMarauderSceneScriptEdit);
+        if (deleted_stage_index > 0) {
+            scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneScriptEdit, deleted_stage_index - 1);
+        }
+        WifiMarauderScriptStage* previous_stage = NULL;
+        WifiMarauderScriptStage* current_stage = app->script->first_stage;
+        uint32_t current_stage_index = 0;
+
+        while (current_stage != NULL && current_stage_index < deleted_stage_index) {
+            previous_stage = current_stage;
+            current_stage = current_stage->next_stage;
+            current_stage_index++;
+        }
+
+        // Delete the stage
+        if (current_stage != NULL) {
+            if (previous_stage != NULL) {
+                if (current_stage->next_stage != NULL) {
+                    previous_stage->next_stage = current_stage->next_stage;
+                } else {
+                    previous_stage->next_stage = NULL;
+                }
+            } else {
+                if (current_stage->next_stage != NULL) {
+                    app->script->first_stage = current_stage->next_stage;
+                } else {
+                    app->script->first_stage = NULL;
+                }
+            }
+        }
+        app->script_edit_selected_stage = NULL;
+
+        scene_manager_previous_scene(app->scene_manager);
+        return;
+    }
+
+    if (menu_item->select_callback == NULL) {
+        return;
+    }
+    if (menu_item->type == WifiMarauderScriptEditItemTypeNumber) {
+        // Accepts user number input, assigning the value to the reference passed as a parameter
+        menu_item->select_callback(app);
+        scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneScriptStageEdit, index);
+        app->user_input_type = WifiMarauderUserInputTypeNumber;
+        scene_manager_next_scene(app->scene_manager, WifiMarauderSceneUserInput);
+    } else if (menu_item->type == WifiMarauderScriptEditItemTypeString) {
+        // Accepts user string input, assigning the value to the reference passed as a parameter
+        menu_item->select_callback(app);
+        scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneScriptStageEdit, index);
+        app->user_input_type = WifiMarauderUserInputTypeString;
+        scene_manager_next_scene(app->scene_manager, WifiMarauderSceneUserInput);
+    } else if (menu_item->type == WifiMarauderScriptEditItemTypeListString) {
+        // Accepts the strings that compose the list
+        menu_item->select_callback(app);
+        wifi_marauder_scene_script_stage_edit_create_list_strings(app, *app->script_stage_edit_strings_reference, *app->script_stage_edit_string_count_reference);
+        scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneScriptStageEdit, index);
+        scene_manager_next_scene(app->scene_manager, WifiMarauderSceneScriptStageEditList);
+    } else if (menu_item->type == WifiMarauderScriptEditItemTypeListNumber) {
+        // Accepts the numbers that compose the list
+        menu_item->select_callback(app);
+        wifi_marauder_scene_script_stage_edit_create_list_numbers(app, *app->script_stage_edit_numbers_reference, *app->script_stage_edit_number_count_reference);
+        scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneScriptStageEdit, index);
+        scene_manager_next_scene(app->scene_manager, WifiMarauderSceneScriptStageEditList);
+    }
+}
+
+// Scan stage =============================================================================================================================
+static void wifi_marauder_scan_stage_type_setup_callback(VariableItem* item) {
+    WifiMarauderApp* app = variable_item_get_context(item);
+    WifiMarauderScriptStageScan* stage = app->script_edit_selected_stage->stage;
+    variable_item_set_current_value_index(item, stage->type);
+}
+
+static void wifi_marauder_scan_stage_type_change_callback(VariableItem* item) {
+    WifiMarauderApp* app = variable_item_get_context(item);
+
+    // Get menu item
+    uint8_t current_stage_index = variable_item_list_get_selected_item_index(app->script_stage_edit_list);
+    const WifiMarauderScriptEditItem* menu_item = &stage_items[current_stage_index];
+
+    // Defines the text of the selected option
+    uint8_t option_index = variable_item_get_current_value_index(item);
+    variable_item_set_current_value_text(item, menu_item->options[option_index]);
+
+    // Updates the attribute value of the current stage
+    WifiMarauderScriptStageScan* stage = app->script_edit_selected_stage->stage;
+    stage->type = option_index;
+}
+
+static void wifi_marauder_scan_stage_channel_setup_callback(VariableItem* item) {
+    WifiMarauderApp* app = variable_item_get_context(item);
+    WifiMarauderScriptStageScan* stage = app->script_edit_selected_stage->stage;
+    if (stage->channel >= 0 && stage->channel < 12) {
+        variable_item_set_current_value_index(item, stage->channel);
+    } else {
+        variable_item_set_current_value_index(item, 0);
+    }
+}
+
+static void wifi_marauder_scan_stage_channel_change_callback(VariableItem* item) {
+    WifiMarauderApp* app = variable_item_get_context(item);
+
+    // Get menu item
+    uint8_t current_stage_index = variable_item_list_get_selected_item_index(app->script_stage_edit_list);
+    const WifiMarauderScriptEditItem* menu_item = &stage_items[current_stage_index];
+
+    // Defines the text of the selected option
+    uint8_t option_index = variable_item_get_current_value_index(item);
+    variable_item_set_current_value_text(item, menu_item->options[option_index]);
+
+    // Updates the attribute value of the current stage
+    WifiMarauderScriptStageScan* stage = app->script_edit_selected_stage->stage;
+    stage->channel = option_index;
+}
+
+static void wifi_marauder_scan_stage_timeout_setup_callback(VariableItem* item) {
+    WifiMarauderApp* app = variable_item_get_context(item);
+    WifiMarauderScriptStageScan* stage = app->script_edit_selected_stage->stage;
+    char timeout_str[32];
+    snprintf(timeout_str, sizeof(timeout_str), "%d", stage->timeout);
+    variable_item_set_current_value_text(item, timeout_str);
+}
+
+static void wifi_marauder_scan_stage_timeout_select_callback(WifiMarauderApp* app) {
+    WifiMarauderScriptStageScan* stage_scan = app->script_edit_selected_stage->stage;
+    app->user_input_number_reference = &stage_scan->timeout;
+}
+
+WifiMarauderScriptEditItem scan_items[] = {
+    {
+        "Type", WifiMarauderScriptEditItemTypeOptionsString, 2, {"ap", "station"},
+        wifi_marauder_scan_stage_type_setup_callback, wifi_marauder_scan_stage_type_change_callback, NULL
+    },
+    {
+        "Channel", WifiMarauderScriptEditItemTypeOptionsNumber, 12, {"none", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11"},
+        wifi_marauder_scan_stage_channel_setup_callback, wifi_marauder_scan_stage_channel_change_callback, NULL
+    },
+    {
+        "Timeout", WifiMarauderScriptEditItemTypeNumber, 1, {NULL},
+        wifi_marauder_scan_stage_timeout_setup_callback, NULL, wifi_marauder_scan_stage_timeout_select_callback
+    }
+};
+
+// Select stage =============================================================================================================================
+static void wifi_marauder_select_stage_type_setup_callback(VariableItem* item) {
+    WifiMarauderApp* app = variable_item_get_context(item);
+    WifiMarauderScriptStageSelect* stage = app->script_edit_selected_stage->stage;
+    variable_item_set_current_value_index(item, stage->type);
+}
+
+static void wifi_marauder_select_stage_type_change_callback(VariableItem* item) {
+    WifiMarauderApp* app = variable_item_get_context(item);
+
+    // Get menu item
+    uint8_t current_stage_index = variable_item_list_get_selected_item_index(app->script_stage_edit_list);
+    const WifiMarauderScriptEditItem* menu_item = &stage_items[current_stage_index];
+
+    // Defines the text of the selected option
+    uint8_t option_index = variable_item_get_current_value_index(item);
+    variable_item_set_current_value_text(item, menu_item->options[option_index]);
+
+    // Updates the attribute value of the current stage
+    WifiMarauderScriptStageSelect* stage = app->script_edit_selected_stage->stage;
+    stage->type = option_index;
+}
+
+static void wifi_marauder_select_stage_filter_setup_callback(VariableItem* item) {
+    WifiMarauderApp* app = variable_item_get_context(item);
+    WifiMarauderScriptStageSelect* stage = app->script_edit_selected_stage->stage;
+    variable_item_set_current_value_text(item, stage->filter);
+}
+
+static void wifi_marauder_select_stage_filter_select_callback(WifiMarauderApp* app) {
+    WifiMarauderScriptStageSelect* stage_select = app->script_edit_selected_stage->stage;
+    if (stage_select->filter == NULL) {
+        stage_select->filter = malloc(128);
+    }
+    app->user_input_string_reference = &stage_select->filter;
+}
+
+static void wifi_marauder_select_stage_indexes_select_callback(WifiMarauderApp* app) {
+    WifiMarauderScriptStageSelect* stage_select = app->script_edit_selected_stage->stage;
+    app->script_stage_edit_numbers_reference = &stage_select->indexes;
+    app->script_stage_edit_number_count_reference = &stage_select->index_count;
+}
+
+WifiMarauderScriptEditItem select_items[] = {
+    {
+        "Type", WifiMarauderScriptEditItemTypeOptionsString, 2, {"ap", "station"},
+        wifi_marauder_select_stage_type_setup_callback, wifi_marauder_select_stage_type_change_callback, NULL
+    },
+    {
+        "Filter", WifiMarauderScriptEditItemTypeString, 1, {NULL},
+        wifi_marauder_select_stage_filter_setup_callback, NULL, wifi_marauder_select_stage_filter_select_callback
+    },
+    {
+        "Indexes", WifiMarauderScriptEditItemTypeListNumber, 1, {NULL},
+        NULL, NULL, wifi_marauder_select_stage_indexes_select_callback
+    }
+};
+
+// Deauth stage ===========================================================================================================================
+static void wifi_marauder_deauth_stage_timeout_setup_callback(VariableItem* item) {
+    WifiMarauderApp* app = variable_item_get_context(item);
+    WifiMarauderScriptStageDeauth* stage = app->script_edit_selected_stage->stage;
+    char timeout_str[32];
+    snprintf(timeout_str, sizeof(timeout_str), "%d", stage->timeout);
+    variable_item_set_current_value_text(item, timeout_str);
+}
+
+static void wifi_marauder_deauth_stage_timeout_select_callback(WifiMarauderApp* app) {
+    WifiMarauderScriptStageDeauth* stage_deauth = app->script_edit_selected_stage->stage;
+    app->user_input_number_reference = &stage_deauth->timeout;
+}
+
+WifiMarauderScriptEditItem deauth_items[] = {
+    {
+        "Timeout", WifiMarauderScriptEditItemTypeNumber, 1, {NULL},
+        wifi_marauder_deauth_stage_timeout_setup_callback, NULL, wifi_marauder_deauth_stage_timeout_select_callback
+    }
+};
+
+// Probe stage ============================================================================================================================
+static void wifi_marauder_probe_stage_timeout_setup_callback(VariableItem* item) {
+    WifiMarauderApp* app = variable_item_get_context(item);
+    WifiMarauderScriptStageProbe* stage = app->script_edit_selected_stage->stage;
+    char timeout_str[32];
+    snprintf(timeout_str, sizeof(timeout_str), "%d", stage->timeout);
+    variable_item_set_current_value_text(item, timeout_str);
+}
+
+static void wifi_marauder_probe_stage_timeout_select_callback(WifiMarauderApp* app) {
+    WifiMarauderScriptStageProbe* stage_probe = app->script_edit_selected_stage->stage;
+    app->user_input_number_reference = &stage_probe->timeout;
+}
+
+WifiMarauderScriptEditItem probe_items[] = {
+    {
+        "Timeout", WifiMarauderScriptEditItemTypeNumber, 1, {NULL},
+        wifi_marauder_probe_stage_timeout_setup_callback, NULL, wifi_marauder_probe_stage_timeout_select_callback
+    }
+};
+
+// Sniff RAW stage ========================================================================================================================
+static void wifi_marauder_sniffraw_stage_timeout_setup_callback(VariableItem* item) {
+    WifiMarauderApp* app = variable_item_get_context(item);
+    WifiMarauderScriptStageSniffRaw* stage = app->script_edit_selected_stage->stage;
+    char timeout_str[32];
+    snprintf(timeout_str, sizeof(timeout_str), "%d", stage->timeout);
+    variable_item_set_current_value_text(item, timeout_str);
+}
+
+static void wifi_marauder_sniffraw_stage_timeout_select_callback(WifiMarauderApp* app) {
+    WifiMarauderScriptStageSniffRaw* stage_sniffraw = app->script_edit_selected_stage->stage;
+    app->user_input_number_reference = &stage_sniffraw->timeout;
+}
+
+WifiMarauderScriptEditItem sniffraw_items[] = {
+    {
+        "Timeout", WifiMarauderScriptEditItemTypeNumber, 1, {NULL},
+        wifi_marauder_sniffraw_stage_timeout_setup_callback, NULL, wifi_marauder_sniffraw_stage_timeout_select_callback
+    }
+};
+
+// Sniff Beacon stage =====================================================================================================================
+static void wifi_marauder_sniffbeacon_stage_timeout_setup_callback(VariableItem* item) {
+    WifiMarauderApp* app = variable_item_get_context(item);
+    WifiMarauderScriptStageSniffBeacon* stage = app->script_edit_selected_stage->stage;
+    char timeout_str[32];
+    snprintf(timeout_str, sizeof(timeout_str), "%d", stage->timeout);
+    variable_item_set_current_value_text(item, timeout_str);
+}
+
+static void wifi_marauder_sniffbeacon_stage_timeout_select_callback(WifiMarauderApp* app) {
+    WifiMarauderScriptStageSniffBeacon* stage_sniffbeacon = app->script_edit_selected_stage->stage;
+    app->user_input_number_reference = &stage_sniffbeacon->timeout;
+}
+
+WifiMarauderScriptEditItem sniffbeacon_items[] = {
+    {
+        "Timeout", WifiMarauderScriptEditItemTypeNumber, 1, {NULL},
+        wifi_marauder_sniffbeacon_stage_timeout_setup_callback, NULL, wifi_marauder_sniffbeacon_stage_timeout_select_callback
+    }
+};
+
+// Sniff Deauth stage =====================================================================================================================
+static void wifi_marauder_sniffdeauth_stage_timeout_setup_callback(VariableItem* item) {
+    WifiMarauderApp* app = variable_item_get_context(item);
+    WifiMarauderScriptStageSniffDeauth* stage = app->script_edit_selected_stage->stage;
+    char timeout_str[32];
+    snprintf(timeout_str, sizeof(timeout_str), "%d", stage->timeout);
+    variable_item_set_current_value_text(item, timeout_str);
+}
+
+static void wifi_marauder_sniffdeauth_stage_timeout_select_callback(WifiMarauderApp* app) {
+    WifiMarauderScriptStageSniffDeauth* stage_sniffdeauth = app->script_edit_selected_stage->stage;
+    app->user_input_number_reference = &stage_sniffdeauth->timeout;
+}
+
+WifiMarauderScriptEditItem sniffdeauth_items[] = {
+    {
+        "Timeout", WifiMarauderScriptEditItemTypeNumber, 1, {NULL},
+        wifi_marauder_sniffdeauth_stage_timeout_setup_callback, NULL, wifi_marauder_sniffdeauth_stage_timeout_select_callback
+    }
+};
+
+// Sniff Esp stage ========================================================================================================================
+static void wifi_marauder_sniffesp_stage_timeout_setup_callback(VariableItem* item) {
+    WifiMarauderApp* app = variable_item_get_context(item);
+    WifiMarauderScriptStageSniffEsp* stage = app->script_edit_selected_stage->stage;
+    char timeout_str[32];
+    snprintf(timeout_str, sizeof(timeout_str), "%d", stage->timeout);
+    variable_item_set_current_value_text(item, timeout_str);
+}
+
+static void wifi_marauder_sniffesp_stage_timeout_select_callback(WifiMarauderApp* app) {
+    WifiMarauderScriptStageSniffEsp* stage_sniffesp = app->script_edit_selected_stage->stage;
+    app->user_input_number_reference = &stage_sniffesp->timeout;
+}
+
+WifiMarauderScriptEditItem sniffesp_items[] = {
+    {
+        "Timeout", WifiMarauderScriptEditItemTypeNumber, 1, {NULL},
+        wifi_marauder_sniffesp_stage_timeout_setup_callback, NULL, wifi_marauder_sniffesp_stage_timeout_select_callback
+    }
+};
+
+// Sniff PMKID stage ======================================================================================================================
+static void wifi_marauder_sniffpmkid_stage_force_deauth_setup_callback(VariableItem* item) {
+    WifiMarauderApp* app = variable_item_get_context(item);
+    WifiMarauderScriptStageSniffPmkid* stage = app->script_edit_selected_stage->stage;
+    variable_item_set_current_value_index(item, stage->force_deauth);
+}
+
+static void wifi_marauder_sniffpmkid_stage_force_deauth_change_callback(VariableItem* item) {
+    WifiMarauderApp* app = variable_item_get_context(item);
+
+    // Get menu item
+    uint8_t current_stage_index = variable_item_list_get_selected_item_index(app->script_stage_edit_list);
+    const WifiMarauderScriptEditItem* menu_item = &stage_items[current_stage_index];
+
+    // Defines the text of the selected option
+    uint8_t option_index = variable_item_get_current_value_index(item);
+    variable_item_set_current_value_text(item, menu_item->options[option_index]);
+
+    // Updates the attribute value of the current stage
+    WifiMarauderScriptStageSniffPmkid* stage = app->script_edit_selected_stage->stage;
+    stage->force_deauth = option_index;
+}
+
+static void wifi_marauder_sniffpmkid_stage_channel_setup_callback(VariableItem* item) {
+    WifiMarauderApp* app = variable_item_get_context(item);
+    WifiMarauderScriptStageSniffPmkid* stage = app->script_edit_selected_stage->stage;
+    if (stage->channel >= 0 && stage->channel < 12) {
+        variable_item_set_current_value_index(item, stage->channel);
+    } else {
+        variable_item_set_current_value_index(item, 0);
+    }
+}
+
+static void wifi_marauder_sniffpmkid_stage_channel_change_callback(VariableItem* item) {
+    WifiMarauderApp* app = variable_item_get_context(item);
+
+    // Get menu item
+    uint8_t current_stage_index = variable_item_list_get_selected_item_index(app->script_stage_edit_list);
+    const WifiMarauderScriptEditItem* menu_item = &stage_items[current_stage_index];
+
+    // Defines the text of the selected option
+    uint8_t option_index = variable_item_get_current_value_index(item);
+    variable_item_set_current_value_text(item, menu_item->options[option_index]);
+
+    // Updates the attribute value of the current stage
+    WifiMarauderScriptStageSniffPmkid* stage = app->script_edit_selected_stage->stage;
+    stage->channel = option_index;
+}
+
+static void wifi_marauder_sniffpmkid_stage_timeout_setup_callback(VariableItem* item) {
+    WifiMarauderApp* app = variable_item_get_context(item);
+    WifiMarauderScriptStageSniffPmkid* stage = app->script_edit_selected_stage->stage;
+    char timeout_str[32];
+    snprintf(timeout_str, sizeof(timeout_str), "%d", stage->timeout);
+    variable_item_set_current_value_text(item, timeout_str);
+}
+
+static void wifi_marauder_sniffpmkid_stage_timeout_select_callback(WifiMarauderApp* app) {
+    WifiMarauderScriptStageSniffPmkid* stage_sniffpmkid = app->script_edit_selected_stage->stage;
+    app->user_input_number_reference = &stage_sniffpmkid->timeout;
+}
+
+WifiMarauderScriptEditItem sniffpmkid_items[] = {
+    {
+        "Force deauth", WifiMarauderScriptEditItemTypeOptionsString, 2, {"no", "yes"},
+        wifi_marauder_sniffpmkid_stage_force_deauth_setup_callback, wifi_marauder_sniffpmkid_stage_force_deauth_change_callback, NULL
+    },
+    {
+        "Channel", WifiMarauderScriptEditItemTypeOptionsNumber, 12, {"none", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11"},
+        wifi_marauder_sniffpmkid_stage_channel_setup_callback, wifi_marauder_sniffpmkid_stage_channel_change_callback, NULL
+    },
+    {
+        "Timeout", WifiMarauderScriptEditItemTypeNumber, 1, {NULL},
+        wifi_marauder_sniffpmkid_stage_timeout_setup_callback, NULL, wifi_marauder_sniffpmkid_stage_timeout_select_callback
+    }
+};
+
+// Sniff Pwn stage ========================================================================================================================
+static void wifi_marauder_sniffpwn_stage_timeout_setup_callback(VariableItem* item) {
+    WifiMarauderApp* app = variable_item_get_context(item);
+    WifiMarauderScriptStageSniffPwn* stage = app->script_edit_selected_stage->stage;
+    char timeout_str[32];
+    snprintf(timeout_str, sizeof(timeout_str), "%d", stage->timeout);
+    variable_item_set_current_value_text(item, timeout_str);
+}
+
+static void wifi_marauder_sniffpwn_stage_timeout_select_callback(WifiMarauderApp* app) {
+    WifiMarauderScriptStageSniffPwn* stage_sniffpwn = app->script_edit_selected_stage->stage;
+    app->user_input_number_reference = &stage_sniffpwn->timeout;
+}
+
+WifiMarauderScriptEditItem sniffpwn_items[] = {
+    {
+        "Timeout", WifiMarauderScriptEditItemTypeNumber, 1, {NULL},
+        wifi_marauder_sniffpwn_stage_timeout_setup_callback, NULL, wifi_marauder_sniffpwn_stage_timeout_select_callback
+    }
+};
+
+// Beacon List stage ======================================================================================================================
+static void wifi_marauder_beaconlist_stage_ssids_select_callback(WifiMarauderApp* app) {
+    WifiMarauderScriptStageBeaconList* stage_beaconlist = app->script_edit_selected_stage->stage;
+    app->script_stage_edit_strings_reference = &stage_beaconlist->ssids;
+    app->script_stage_edit_string_count_reference = &stage_beaconlist->ssid_count;
+}
+
+static void wifi_marauder_beaconlist_stage_random_ssids_setup_callback(VariableItem* item) {
+    WifiMarauderApp* app = variable_item_get_context(item);
+    WifiMarauderScriptStageBeaconList* stage = app->script_edit_selected_stage->stage;
+    char random_ssids_str[32];
+    snprintf(random_ssids_str, sizeof(random_ssids_str), "%d", stage->random_ssids);
+    variable_item_set_current_value_text(item, random_ssids_str);
+}
+
+static void wifi_marauder_beaconlist_stage_random_ssids_select_callback(WifiMarauderApp* app) {
+    WifiMarauderScriptStageBeaconList* stage_beaconlist = app->script_edit_selected_stage->stage;
+    app->user_input_number_reference = &stage_beaconlist->random_ssids;
+}
+
+static void wifi_marauder_beaconlist_stage_timeout_setup_callback(VariableItem* item) {
+    WifiMarauderApp* app = variable_item_get_context(item);
+    WifiMarauderScriptStageBeaconList* stage = app->script_edit_selected_stage->stage;
+    char timeout_str[32];
+    snprintf(timeout_str, sizeof(timeout_str), "%d", stage->timeout);
+    variable_item_set_current_value_text(item, timeout_str);
+}
+
+static void wifi_marauder_beaconlist_stage_timeout_select_callback(WifiMarauderApp* app) {
+    WifiMarauderScriptStageBeaconList* stage_beaconlist = app->script_edit_selected_stage->stage;
+    app->user_input_number_reference = &stage_beaconlist->timeout;
+}
+
+WifiMarauderScriptEditItem beaconlist_items[] = {
+    {
+        "SSIDs", WifiMarauderScriptEditItemTypeListString, 1, {NULL},
+        NULL, NULL, wifi_marauder_beaconlist_stage_ssids_select_callback
+    },
+    {
+        "Generate random", WifiMarauderScriptEditItemTypeNumber, 1, {NULL},
+        wifi_marauder_beaconlist_stage_random_ssids_setup_callback, NULL, wifi_marauder_beaconlist_stage_random_ssids_select_callback
+    },
+    {
+        "Timeout", WifiMarauderScriptEditItemTypeNumber, 1, {NULL},
+        wifi_marauder_beaconlist_stage_timeout_setup_callback, NULL, wifi_marauder_beaconlist_stage_timeout_select_callback
+    }
+};
+
+// Beacon List stage ======================================================================================================================
+static void wifi_marauder_beaconap_stage_timeout_setup_callback(VariableItem* item) {
+    WifiMarauderApp* app = variable_item_get_context(item);
+    WifiMarauderScriptStageBeaconAp* stage = app->script_edit_selected_stage->stage;
+    char timeout_str[32];
+    snprintf(timeout_str, sizeof(timeout_str), "%d", stage->timeout);
+    variable_item_set_current_value_text(item, timeout_str);
+}
+
+static void wifi_marauder_beaconap_stage_timeout_select_callback(WifiMarauderApp* app) {
+    WifiMarauderScriptStageBeaconAp* stage_beaconap = app->script_edit_selected_stage->stage;
+    app->user_input_number_reference = &stage_beaconap->timeout;
+}
+
+WifiMarauderScriptEditItem beaconap_items[] = {
+    {
+        "Timeout", WifiMarauderScriptEditItemTypeNumber, 1, {NULL},
+        wifi_marauder_beaconap_stage_timeout_setup_callback, NULL, wifi_marauder_beaconap_stage_timeout_select_callback
+    }
+};
+
+// ========================================================================================================================================
+void wifi_marauder_scene_script_stage_edit_setup(WifiMarauderApp* app) {
+    switch (app->script_edit_selected_stage->type) {
+        case WifiMarauderScriptStageTypeScan:
+            stage_items = scan_items;
+            num_stage_items = 3;
+            break;
+        case WifiMarauderScriptStageTypeSelect:
+            stage_items = select_items;
+            num_stage_items = 3;
+            break;
+        case WifiMarauderScriptStageTypeDeauth:
+            stage_items = deauth_items;
+            num_stage_items = 1;
+            break;
+        case WifiMarauderScriptStageTypeProbe:
+            stage_items = probe_items;
+            num_stage_items = 1;
+            break;
+        case WifiMarauderScriptStageTypeSniffRaw:
+            stage_items = sniffraw_items;
+            num_stage_items = 1;
+            break;
+        case WifiMarauderScriptStageTypeSniffBeacon:
+            stage_items = sniffbeacon_items;
+            num_stage_items = 1;
+            break;
+        case WifiMarauderScriptStageTypeSniffDeauth:
+            stage_items = sniffdeauth_items;
+            num_stage_items = 1;
+            break;
+        case WifiMarauderScriptStageTypeSniffEsp:
+            stage_items = sniffesp_items;
+            num_stage_items = 1;
+            break;
+        case WifiMarauderScriptStageTypeSniffPmkid:
+            stage_items = sniffpmkid_items;
+            num_stage_items = 3;
+            break;
+        case WifiMarauderScriptStageTypeSniffPwn:
+            stage_items = sniffpwn_items;
+            num_stage_items = 1;
+            break;
+        case WifiMarauderScriptStageTypeBeaconList:
+            stage_items = beaconlist_items;
+            num_stage_items = 3;
+            break;
+        case WifiMarauderScriptStageTypeBeaconAp:
+            stage_items = beaconap_items;
+            num_stage_items = 1;
+            break;
+        default:
+            break;
+    }
+}
+
+void wifi_marauder_scene_script_stage_edit_on_enter(void* context) {
+    WifiMarauderApp* app = context;
+    VariableItemList* script_stage_edit_list = app->script_stage_edit_list;
+
+    variable_item_list_set_enter_callback(app->script_stage_edit_list, wifi_marauder_scene_script_stage_edit_list_enter_callback, app);
+    wifi_marauder_scene_script_stage_edit_setup(app);
+
+    if (stage_items != NULL) {
+        for (uint32_t i = 0; i < num_stage_items; i++) {
+            WifiMarauderScriptEditItem* stage_item = &stage_items[i];
+
+            // Changes the list item to handle it in callbacks
+            VariableItem* list_item = variable_item_list_add(app->script_stage_edit_list, stage_item->name, stage_item->num_options, stage_item->change_callback, app);
+
+            variable_item_list_set_selected_item(app->script_stage_edit_list, i);
+            if (stage_item->setup_callback != NULL) {
+                stage_item->setup_callback(list_item);
+            }
+            if (stage_item->change_callback != NULL) {
+                stage_item->change_callback(list_item);
+            }
+        }
+    }
+
+    variable_item_list_add(app->script_stage_edit_list, "[-] DELETE STAGE", 0, NULL, app);
+
+    variable_item_list_set_selected_item(script_stage_edit_list, scene_manager_get_scene_state(app->scene_manager, WifiMarauderSceneScriptStageEdit));
+    view_dispatcher_switch_to_view(app->view_dispatcher, WifiMarauderAppViewScriptStageEdit);
+}
+
+bool wifi_marauder_scene_script_stage_edit_on_event(void* context, SceneManagerEvent event) {
+    UNUSED(context);
+    UNUSED(event);
+    return false;
+}
+
+void wifi_marauder_scene_script_stage_edit_on_exit(void* context) {
+    WifiMarauderApp* app = context;
+    variable_item_list_reset(app->script_stage_edit_list);
+}

+ 108 - 0
applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_user_input.c

@@ -0,0 +1,108 @@
+#include "../wifi_marauder_app_i.h"
+
+void wifi_marauder_scene_user_input_callback(void* context) {
+    WifiMarauderApp* app = context;
+
+    File* file = NULL;
+    char* file_path = NULL;
+
+    switch (app->user_input_type) {
+        // Writes the string value of the reference
+        case WifiMarauderUserInputTypeString:
+            if (app->user_input_string_reference != NULL) {
+                strncpy(*app->user_input_string_reference, app->user_input_store, strlen(app->user_input_store) + 1);
+                app->user_input_string_reference = NULL;
+            }
+            break;
+        // Writes the numerical value of the reference
+        case WifiMarauderUserInputTypeNumber:
+            if (app->user_input_number_reference != NULL) {
+                *app->user_input_number_reference = atoi(app->user_input_store);
+                app->user_input_number_reference = NULL;
+            }
+            break;
+        // Creates a file with the name entered by the user, if it does not exist
+        case WifiMarauderUserInputTypeFileName:
+            file = storage_file_alloc(app->storage);
+            // Use application directory if not specified
+            if (app->user_input_file_dir == NULL) {
+                app->user_input_file_dir = strdup(MARAUDER_APP_FOLDER);
+            }
+            if (app->user_input_file_extension != NULL) {
+                size_t file_path_len = strlen(app->user_input_file_dir) + strlen(app->user_input_store) + strlen(app->user_input_file_extension) + 3;
+                file_path = (char*)malloc(file_path_len);
+                snprintf(file_path, file_path_len, "%s/%s.%s", app->user_input_file_dir, app->user_input_store, app->user_input_file_extension);
+            } else {
+                size_t file_path_len = strlen(app->user_input_file_dir) + strlen(app->user_input_store) + 2;
+                file_path = (char*)malloc(file_path_len);
+                snprintf(file_path, file_path_len, "%s/%s", app->user_input_file_dir, app->user_input_store);
+            }
+            if (storage_file_open(file, file_path, FSAM_WRITE, FSOM_CREATE_NEW)) {
+                storage_file_close(file);
+            }
+            // Free memory
+            free(app->user_input_file_dir);
+            app->user_input_file_dir = NULL;
+            free(app->user_input_file_extension);
+            app->user_input_file_extension = NULL;
+            free(file_path);
+            storage_file_free(file);
+            break;
+        default:
+            break;
+    }
+
+    scene_manager_previous_scene(app->scene_manager);
+}
+
+void wifi_marauder_scene_user_input_on_enter(void* context) {
+    WifiMarauderApp* app = context;
+
+    switch (app->user_input_type) {
+        // Loads the string value of the reference
+        case WifiMarauderUserInputTypeString:
+            text_input_set_header_text(app->user_input, "Enter value:");
+            if (app->user_input_string_reference != NULL) {
+                strncpy(app->user_input_store, *app->user_input_string_reference, strlen(*app->user_input_string_reference) + 1);
+            }
+            break;
+        // Loads the numerical value of the reference
+        case WifiMarauderUserInputTypeNumber:
+            text_input_set_header_text(app->user_input, "Enter a valid number:");
+            if (app->user_input_number_reference != NULL) {
+                char number_str[32];
+                snprintf(number_str, sizeof(number_str), "%d", *app->user_input_number_reference);
+                strncpy(app->user_input_store, number_str, strlen(number_str) + 1);
+            }
+            break;
+        // File name
+        case WifiMarauderUserInputTypeFileName:
+            text_input_set_header_text(app->user_input, "Enter file name:");
+            break;
+        default:
+            scene_manager_previous_scene(app->scene_manager);
+            return;
+    }
+
+    text_input_set_result_callback(
+        app->user_input,
+        wifi_marauder_scene_user_input_callback,
+        app,
+        app->user_input_store,
+        WIFI_MARAUDER_USER_INPUT_STORE_SIZE,
+        false);
+
+    view_dispatcher_switch_to_view(app->view_dispatcher, WifiMarauderAppViewUserInput);
+}
+
+bool wifi_marauder_scene_user_input_on_event(void* context, SceneManagerEvent event) {
+    UNUSED(context);
+    UNUSED(event);
+    return false;
+}
+
+void wifi_marauder_scene_user_input_on_exit(void* context) {
+    WifiMarauderApp* app = context;
+    memset(app->user_input_store, 0, sizeof(app->user_input_store));
+    text_input_reset(app->user_input);
+}

+ 15 - 28
applications/external/wifi_marauder_companion/script/wifi_marauder_script.c

@@ -1,12 +1,6 @@
 #include "../wifi_marauder_app_i.h"
 #include "wifi_marauder_script.h"
 
-#define WIFI_MARAUDER_DEFAULT_TIMEOUT_SCAN 15
-#define WIFI_MARAUDER_DEFAULT_TIMEOUT_DEAUTH 30
-#define WIFI_MARAUDER_DEFAULT_TIMEOUT_PROBE 60
-#define WIFI_MARAUDER_DEFAULT_TIMEOUT_SNIFF 60
-#define WIFI_MARAUDER_DEFAULT_TIMEOUT_BEACON 60
-
 WifiMarauderScript *wifi_marauder_script_alloc() {
     WifiMarauderScript *script = (WifiMarauderScript *) malloc(sizeof(WifiMarauderScript));
     if (script == NULL) {
@@ -95,7 +89,6 @@ WifiMarauderScriptStageSelect* _wifi_marauder_script_get_stage_select(cJSON *sta
     cJSON *type_json = cJSON_GetObjectItemCaseSensitive(select_stage_json, "type");
     cJSON *filter_json = cJSON_GetObjectItemCaseSensitive(select_stage_json, "filter");
     cJSON *indexes_json = cJSON_GetObjectItemCaseSensitive(select_stage_json, "indexes");
-    cJSON *index_json = cJSON_GetObjectItemCaseSensitive(select_stage_json, "index");
     cJSON *allow_repeat_json = cJSON_GetObjectItemCaseSensitive(select_stage_json, "allow_repeat");
 
     if (!cJSON_IsString(type_json) || !cJSON_IsString(filter_json)) {
@@ -120,11 +113,7 @@ WifiMarauderScriptStageSelect* _wifi_marauder_script_get_stage_select(cJSON *sta
     stage_select->filter = filter_str;
     stage_select->allow_repeat = cJSON_IsBool(allow_repeat_json) ? allow_repeat_json->valueint : true;
 
-    if (cJSON_IsNumber(index_json)) {
-        int* indexes = (int*) malloc(sizeof(int));
-        indexes[0] = index_json->valueint;
-        stage_select->indexes = indexes;
-    } else if (cJSON_IsArray(indexes_json)) {
+    if (cJSON_IsArray(indexes_json)) {
         int indexes_size = cJSON_GetArraySize(indexes_json);
         int* indexes = (int*) malloc(indexes_size * sizeof(int));
         for (int i = 0; i < indexes_size; i++) {
@@ -134,8 +123,10 @@ WifiMarauderScriptStageSelect* _wifi_marauder_script_get_stage_select(cJSON *sta
             }
         }
         stage_select->indexes = indexes;
+        stage_select->index_count = indexes_size;
     } else {
         stage_select->indexes = NULL;
+        stage_select->index_count = 0;
     }
 
     return stage_select;
@@ -404,6 +395,8 @@ WifiMarauderScript *wifi_marauder_script_parse_raw(const char* json_raw) {
 WifiMarauderScript *wifi_marauder_script_parse_json(Storage* storage, const char* file_path) {
     WifiMarauderScript *script = NULL;
     File* script_file = storage_file_alloc(storage);
+    FuriString* script_name = furi_string_alloc();
+    path_extract_filename_no_ext(file_path, script_name);
 
     if (storage_file_open(script_file, file_path, FSAM_READ, FSOM_OPEN_EXISTING)) {
         uint32_t file_size = storage_file_size(script_file);
@@ -412,16 +405,14 @@ WifiMarauderScript *wifi_marauder_script_parse_json(Storage* storage, const char
         json_buffer[bytes_read] = '\0';
         
         script = wifi_marauder_script_parse_raw(json_buffer);
-        if (script != NULL) {
-            // Set script name
-            FuriString* script_name = furi_string_alloc();
-            path_extract_filename_no_ext(file_path, script_name);
-            script->name = strdup(furi_string_get_cstr(script_name));
-            furi_string_free(script_name);
-        }
-        storage_file_close(script_file);
     }
+    if (script == NULL) {
+        script = wifi_marauder_script_create(furi_string_get_cstr(script_name));
+    }
+    script->name = strdup(furi_string_get_cstr(script_name));
 
+    furi_string_free(script_name);
+    storage_file_close(script_file);
     storage_file_free(script_file);
     return script;
 }
@@ -470,16 +461,12 @@ cJSON* _wifi_marauder_script_create_json_select(WifiMarauderScriptStageSelect* s
         cJSON_AddStringToObject(select_json, "filter", select_stage->filter);
     }
     // Indexes
-    if (select_stage->indexes != NULL) {
+    if (select_stage->indexes != NULL && select_stage->index_count > 0) {
         cJSON* indexes_json = cJSON_CreateArray();
-        int* index_ptr = select_stage->indexes;
-        while (*index_ptr != -1) {
-            cJSON_AddItemToArray(indexes_json, cJSON_CreateNumber(*index_ptr));
-            index_ptr++;
-        }
-        if (cJSON_GetArraySize(indexes_json) > 0) {
-            cJSON_AddItemToObject(select_json, "indexes", indexes_json);
+        for (int i = 0; i < select_stage->index_count; i++) {
+            cJSON_AddItemToArray(indexes_json, cJSON_CreateNumber(select_stage->indexes[i]));
         }
+        cJSON_AddItemToObject(select_json, "indexes", indexes_json);
     }
     return stage_json;
 }

+ 26 - 18
applications/external/wifi_marauder_companion/script/wifi_marauder_script.h

@@ -15,22 +15,19 @@
  * - Create function "void _wifi_marauder_script_execute_????(WifiMarauderScriptStage????* stage)"
  * - Add case in wifi_marauder_script_execute_stage()
  * 
+ * wifi_marauder_scene_script_edit.c
+ * - Add case in wifi_marauder_scene_script_edit_on_enter()
+ *
+ * wifi_marauder_scene_script_stage_add.c
+ * - Create stage creation function and add in wifi_marauder_scene_script_stage_add_on_enter()
+ * 
+ * wifi_marauder_scene_script_stage_edit.c
+ * - Create a list of WifiMarauderScriptEditItem[] items with the attributes on the stage
+ * - Implement the callbacks
+ * - Add case in wifi_marauder_scene_script_stage_edit_setup()
+ * 
  * ----------------------------------------------------------------------------------------------------
- * IMPLEMENTED STAGES (In order of execution):
- * - Scan
- * - Select
- * - Deauth
- * - Probe
- * - Sniff raw
- * - Sniff beacon
- * - Sniff deauth
- * - Sniff Espressif
- * - Sniff PMKID
- * - Sniff Pwnagotchi
- * - Beacon List/Random
- * - Beacon Ap
- * ----------------------------------------------------------------------------------------------------
- * SCRIPT SYNTAX:
+ * SCRIPT SYNTAX (In order of execution):
  * {
  *     "meta": {
  *         "description": "My script",
@@ -47,7 +44,6 @@
  *         "select": {
  *             "type": "ap" | "station" | "ssid",
  *             "filter": "all" | "contains -f '{SSID fragment}' or equals '{SSID}' or ...",
- *             "index": index number,
  *             "indexes": [0, 1, 2, 3...],
  *         },
  *         "deauth": {
@@ -108,6 +104,12 @@
 #include <storage/storage.h>
 #include "cJSON.h"
 
+#define WIFI_MARAUDER_DEFAULT_TIMEOUT_SCAN 15
+#define WIFI_MARAUDER_DEFAULT_TIMEOUT_DEAUTH 30
+#define WIFI_MARAUDER_DEFAULT_TIMEOUT_PROBE 60
+#define WIFI_MARAUDER_DEFAULT_TIMEOUT_SNIFF 60
+#define WIFI_MARAUDER_DEFAULT_TIMEOUT_BEACON 60
+
 typedef enum {
     WifiMarauderScriptBooleanFalse = 0,
     WifiMarauderScriptBooleanTrue = 1,
@@ -130,8 +132,8 @@ typedef enum {
 } WifiMarauderScriptStageType;
 
 typedef enum {
-    WifiMarauderScriptScanTypeAp,
-    WifiMarauderScriptScanTypeStation
+    WifiMarauderScriptScanTypeAp = 0,
+    WifiMarauderScriptScanTypeStation = 1
 } WifiMarauderScriptScanType;
 
 typedef enum {
@@ -157,6 +159,7 @@ typedef struct WifiMarauderScriptStageSelect {
     WifiMarauderScriptSelectType type;
     char* filter;
     int* indexes;
+    int index_count;
     // TODO: Implement a feature to not select the same items in the next iteration of the script
     bool allow_repeat;
 } WifiMarauderScriptStageSelect;
@@ -217,6 +220,11 @@ typedef struct WifiMarauderScript {
     int repeat;
 } WifiMarauderScript;
 
+typedef struct WifiMarauderScriptStageListItem {
+    char* value;
+    struct WifiMarauderScriptStageListItem* next_item;
+} WifiMarauderScriptStageListItem;
+
 WifiMarauderScript* wifi_marauder_script_alloc();
 WifiMarauderScript* wifi_marauder_script_create(const char* script_name);
 WifiMarauderScript* wifi_marauder_script_parse_raw(const char* script_raw);

+ 43 - 5
applications/external/wifi_marauder_companion/wifi_marauder_app.c

@@ -79,12 +79,37 @@ WifiMarauderApp* wifi_marauder_app_alloc() {
         (!storage_file_exists(app->storage, SAVE_PCAP_SETTING_FILEPATH) ||
          !storage_file_exists(app->storage, SAVE_LOGS_SETTING_FILEPATH));
 
+    // User input
+    app->user_input = text_input_alloc();
+    view_dispatcher_add_view(app->view_dispatcher, WifiMarauderAppViewUserInput, text_input_get_view(app->user_input));
+
     // Script select
-    app->script_var_item_list = variable_item_list_alloc();
-    view_dispatcher_add_view(
-        app->view_dispatcher,
-        WifiMarauderAppViewScriptSelect,
-        variable_item_list_get_view(app->script_var_item_list));
+    app->script_select_submenu = submenu_alloc();
+    view_dispatcher_add_view(app->view_dispatcher, WifiMarauderAppViewScriptSelect, submenu_get_view(app->script_select_submenu));
+
+    // Script options
+    app->script_options_submenu = submenu_alloc();
+    view_dispatcher_add_view(app->view_dispatcher, WifiMarauderAppViewScriptOptions, submenu_get_view(app->script_options_submenu));
+
+    // Script confirm delete
+    app->script_confirm_delete_widget = widget_alloc();
+    view_dispatcher_add_view(app->view_dispatcher, WifiMarauderAppViewScriptConfirmDelete, widget_get_view(app->script_confirm_delete_widget));
+
+    // Script stage add
+    app->script_stage_add_submenu = submenu_alloc();
+    view_dispatcher_add_view(app->view_dispatcher, WifiMarauderAppViewScriptStageAdd, submenu_get_view(app->script_stage_add_submenu));
+
+    // Script edit
+    app->script_edit_submenu = submenu_alloc();
+    view_dispatcher_add_view(app->view_dispatcher, WifiMarauderAppViewScriptEdit, submenu_get_view(app->script_edit_submenu));
+
+    // Script stage edit list submenu
+    app->script_stage_edit_list_submenu = submenu_alloc();
+    view_dispatcher_add_view(app->view_dispatcher, WifiMarauderAppViewScriptStageEditList, submenu_get_view(app->script_stage_edit_list_submenu));
+
+    // Script stage edit
+    app->script_stage_edit_list = variable_item_list_alloc();
+    view_dispatcher_add_view(app->view_dispatcher, WifiMarauderAppViewScriptStageEdit, variable_item_list_get_view(app->script_stage_edit_list));
 
     scene_manager_next_scene(app->scene_manager, WifiMarauderSceneStart);
 
@@ -202,10 +227,23 @@ void wifi_marauder_app_free(WifiMarauderApp* app) {
     view_dispatcher_remove_view(app->view_dispatcher, WifiMarauderAppViewTextInput);
     view_dispatcher_remove_view(app->view_dispatcher, WifiMarauderAppViewWidget);
     view_dispatcher_remove_view(app->view_dispatcher, WifiMarauderAppViewScriptSelect);
+    view_dispatcher_remove_view(app->view_dispatcher, WifiMarauderAppViewScriptOptions);
+    view_dispatcher_remove_view(app->view_dispatcher, WifiMarauderAppViewScriptEdit);
+    view_dispatcher_remove_view(app->view_dispatcher, WifiMarauderAppViewScriptStageEdit);
+    view_dispatcher_remove_view(app->view_dispatcher, WifiMarauderAppViewScriptStageEditList);
+    view_dispatcher_remove_view(app->view_dispatcher, WifiMarauderAppViewUserInput);
     widget_free(app->widget);
+    widget_free(app->script_confirm_delete_widget);
     text_box_free(app->text_box);
     furi_string_free(app->text_box_store);
     text_input_free(app->text_input);
+    text_input_free(app->user_input);
+    submenu_free(app->script_select_submenu);
+    submenu_free(app->script_options_submenu);
+    submenu_free(app->script_stage_add_submenu);
+    submenu_free(app->script_edit_submenu);
+    submenu_free(app->script_stage_edit_list_submenu);
+    variable_item_list_free(app->script_stage_edit_list);
     storage_file_free(app->capture_file);
     storage_file_free(app->log_file);
     storage_file_free(app->save_pcap_setting_file);

+ 39 - 2
applications/external/wifi_marauder_companion/wifi_marauder_app_i.h

@@ -16,9 +16,11 @@
 #include <gui/scene_manager.h>
 #include <gui/modules/text_box.h>
 #include <gui/modules/text_input.h>
+#include <gui/modules/submenu.h>
 #include <gui/modules/variable_item_list.h>
 #include <gui/modules/widget.h>
 
+#include <assets_icons.h>
 #include <storage/storage.h>
 #include <lib/toolbox/path.h>
 #include <dialogs/dialogs.h>
@@ -27,6 +29,7 @@
 
 #define WIFI_MARAUDER_TEXT_BOX_STORE_SIZE (4096)
 #define WIFI_MARAUDER_TEXT_INPUT_STORE_SIZE (512)
+#define WIFI_MARAUDER_USER_INPUT_STORE_SIZE (64)
 
 #define MARAUDER_APP_FOLDER_USER "apps_data/marauder"
 #define MARAUDER_APP_FOLDER ANY_PATH(MARAUDER_APP_FOLDER_USER)
@@ -39,6 +42,13 @@
 #define SAVE_PCAP_SETTING_FILEPATH MARAUDER_APP_FOLDER "/save_pcaps_here.setting"
 #define SAVE_LOGS_SETTING_FILEPATH MARAUDER_APP_FOLDER "/save_logs_here.setting"
 
+// TODO: Pass user input to a file of its own
+typedef enum WifiMarauderUserInputType {
+    WifiMarauderUserInputTypeString,
+    WifiMarauderUserInputTypeNumber,
+    WifiMarauderUserInputTypeFileName
+} WifiMarauderUserInputType;
+
 struct WifiMarauderApp {
     Gui* gui;
     ViewDispatcher* view_dispatcher;
@@ -79,13 +89,33 @@ struct WifiMarauderApp {
     bool is_writing_pcap;
     bool is_writing_log;
 
+    // User input
+    TextInput* user_input;
+    char user_input_store[WIFI_MARAUDER_USER_INPUT_STORE_SIZE + 1];
+    WifiMarauderUserInputType user_input_type;
+    char** user_input_string_reference;
+    int* user_input_number_reference;
+    char* user_input_file_dir;
+    char* user_input_file_extension;
+
     // Automation script
     WifiMarauderScript* script;
     WifiMarauderScriptWorker* script_worker;
-    VariableItemList* script_var_item_list;
     FuriString** script_list;
     int script_list_count;
-    int selected_script_index;
+    Submenu* script_select_submenu;
+    Submenu* script_options_submenu;
+    Submenu* script_edit_submenu;
+    Submenu* script_stage_add_submenu;
+    Submenu* script_stage_edit_list_submenu;
+    Widget* script_confirm_delete_widget;
+    WifiMarauderScriptStage* script_edit_selected_stage;
+    VariableItemList* script_stage_edit_list;
+    WifiMarauderScriptStageListItem* script_stage_edit_first_item;
+    char*** script_stage_edit_strings_reference;
+    int* script_stage_edit_string_count_reference;
+    int** script_stage_edit_numbers_reference;
+    int* script_stage_edit_number_count_reference;
 
     // For input source and destination MAC in targeted deauth attack
     int special_case_input_step;
@@ -119,5 +149,12 @@ typedef enum {
     WifiMarauderAppViewConsoleOutput,
     WifiMarauderAppViewTextInput,
     WifiMarauderAppViewWidget,
+    WifiMarauderAppViewUserInput,
     WifiMarauderAppViewScriptSelect,
+    WifiMarauderAppViewScriptOptions,
+    WifiMarauderAppViewScriptEdit,
+    WifiMarauderAppViewScriptConfirmDelete,
+    WifiMarauderAppViewScriptStageAdd,
+    WifiMarauderAppViewScriptStageEdit,
+    WifiMarauderAppViewScriptStageEditList,
 } WifiMarauderAppView;