# -*- mode: python3; indent-tabs-mode: nil; tab-width: 4 -*- # altbeacon.py - protocol handlers for AltBeacon # # This file is part of Scapy # See http://www.secdev.org/projects/scapy for more information # Copyright (C) Michael Farrell # This program is published under a GPLv2 (or later) license # # scapy.contrib.description = AltBeacon BLE proximity beacon # scapy.contrib.status = loads """ scapy.contrib.altbeacon - AltBeacon Bluetooth LE proximity beacons. The AltBeacon specification can be found at: https://github.com/AltBeacon/spec """ from scapy.fields import ByteField, ShortField, SignedByteField, \ StrFixedLenField from scapy.layers.bluetooth import EIR_Hdr, EIR_Manufacturer_Specific_Data, \ UUIDField, LowEnergyBeaconHelper from scapy.packet import Packet # When building beacon frames, one should use their own manufacturer ID. # # However, most software (including the AltBeacon SDK) requires explicitly # registering particular manufacturer IDs to listen to, and the only ID used is # that of Radius Networks (the developer of the specification). # # To maximise compatibility, Scapy's implementation of # LowEnergyBeaconHelper.build_eir (for constructing frames) uses Radius # Networks' manufacturer ID. # # Scapy's implementation of AltBeacon **does not** require a specific # manufacturer ID to detect AltBeacons - it uses # EIR_Manufacturer_Specific_Data.register_magic_payload. RADIUS_NETWORKS_MFG = 0x0118 class AltBeacon(Packet, LowEnergyBeaconHelper): """ AltBeacon broadcast frame type. https://github.com/AltBeacon/spec """ name = "AltBeacon" magic = b"\xBE\xAC" fields_desc = [ StrFixedLenField("header", magic, len(magic)), # The spec says this is 20 bytes, with >=16 bytes being an # organisational unit-specific identifier. However, the Android library # treats this as UUID + uint16 + uint16. UUIDField("id1", None), # Local identifier ShortField("id2", None), ShortField("id3", None), SignedByteField("tx_power", None), ByteField("mfg_reserved", None), ] @classmethod def magic_check(cls, payload): """ Checks if the given payload is for us (starts with our magic string). """ return payload.startswith(cls.magic) def build_eir(self): """Builds a list of EIR messages to wrap this frame.""" # Note: Company ID is not required by spec, but most tools only look # for manufacturer-specific data with Radius Networks' manufacturer ID. return LowEnergyBeaconHelper.base_eir + [ EIR_Hdr() / EIR_Manufacturer_Specific_Data( company_id=RADIUS_NETWORKS_MFG) / self ] EIR_Manufacturer_Specific_Data.register_magic_payload(AltBeacon)