1# SPDX-License-Identifier: GPL-2.0-or-later 2# This file is part of Scapy 3# See https://scapy.net/ for more information 4# Copyright (C) Michael Farrell <micolous+git@gmail.com> 5 6# scapy.contrib.description = iBeacon BLE proximity beacon 7# scapy.contrib.status = loads 8""" 9scapy.contrib.ibeacon - Apple iBeacon Bluetooth LE proximity beacons. 10 11Packet format documentation can be found at at: 12 13* https://en.wikipedia.org/wiki/IBeacon#Packet_Structure_Byte_Map (public) 14* https://developer.apple.com/ibeacon/ (official, requires license) 15 16""" 17 18from scapy.fields import ByteEnumField, ConditionalField, LenField, \ 19 PacketListField, ShortField, SignedByteField, UUIDField 20from scapy.layers.bluetooth import EIR_Hdr, EIR_Manufacturer_Specific_Data, \ 21 LowEnergyBeaconHelper 22from scapy.packet import bind_layers, Packet 23 24APPLE_MFG = 0x004c 25 26 27class Apple_BLE_Submessage(Packet, LowEnergyBeaconHelper): 28 """ 29 A basic Apple submessage. 30 """ 31 32 name = "Apple BLE submessage" 33 fields_desc = [ 34 ByteEnumField("subtype", None, { 35 0x01: "overflow", 36 0x02: "ibeacon", 37 0x05: "airdrop", 38 0x07: "airpods", 39 0x09: "airplay_sink", 40 0x0a: "airplay_src", 41 0x0c: "handoff", 42 0x10: "nearby", 43 }), 44 ConditionalField( 45 # "overflow" messages omit `len` field 46 LenField("len", None, fmt="B"), 47 lambda pkt: pkt.subtype != 0x01 48 ), 49 ] 50 51 def extract_padding(self, s): 52 # Needed to end each EIR_Element packet and make PacketListField work. 53 if self.subtype == 0x01: 54 # Overflow messages are always 16 bytes. 55 return s[:16], s[16:] 56 return s[:self.len], s[self.len:] 57 58 # These methods are here in case you only want to send 1 submessage. 59 # It creates an Apple_BLE_Frame to wrap your (single) Apple_BLE_Submessage. 60 def build_frame(self): 61 """Wraps this submessage in a Apple_BLE_Frame.""" 62 return Apple_BLE_Frame(plist=[self]) 63 64 def build_eir(self): 65 """See Apple_BLE_Frame.build_eir.""" 66 return self.build_frame().build_eir() 67 68 69class Apple_BLE_Frame(Packet, LowEnergyBeaconHelper): 70 """ 71 The wrapper for a BLE manufacturer-specific data advertisement from Apple 72 devices. 73 74 Each advertisement is composed of one or multiple submessages. 75 76 The length of this field comes from the EIR_Hdr. 77 """ 78 name = "Apple BLE broadcast frame" 79 fields_desc = [ 80 PacketListField("plist", None, Apple_BLE_Submessage) 81 ] 82 83 def build_eir(self): 84 """Builds a list of EIR messages to wrap this frame.""" 85 86 return LowEnergyBeaconHelper.base_eir + [ 87 EIR_Hdr() / EIR_Manufacturer_Specific_Data() / self 88 ] 89 90 91class IBeacon_Data(Packet): 92 """ 93 iBeacon broadcast data frame. Composed on top of an Apple_BLE_Submessage. 94 """ 95 name = "iBeacon data" 96 fields_desc = [ 97 UUIDField("uuid", None, uuid_fmt=UUIDField.FORMAT_BE), 98 ShortField("major", None), 99 ShortField("minor", None), 100 SignedByteField("tx_power", None), 101 ] 102 103 104bind_layers(EIR_Manufacturer_Specific_Data, Apple_BLE_Frame, 105 company_id=APPLE_MFG) 106bind_layers(Apple_BLE_Submessage, IBeacon_Data, subtype=2) 107