蓝牙

注解

如果你刚开始使用scapy,从 usage documentation 介绍了如何在以太网和IP上使用scapy。

警告

scapy不支持Windows上的蓝牙接口。

什么是蓝牙?

蓝牙是一种短程的,主要是点对点无线通信协议,在2.4GHz上运行。 `ISM band`_ _.

Bluetooth standards are publicly available 来自于 `Bluetooth Special Interest Group.`_ _

从广义上讲,蓝牙 不同的物理层协议:

蓝牙基本速率(BR)和增强数据速率(EDR)

这些是“经典”的蓝牙物理层。

BR 达到最高721kbit/s的有效速度。这被批准为 IEEE 802.15.1-2002 (V1.1)和 -2005 (V1.2)。

EDR 作为蓝牙2.0(2004)的可选功能引入。它可以达到2.1兆位/秒的有效速度,比BR的功耗更低。

在蓝牙4.0及更高版本中,不支持 低能 接口,除非它们标记为 dual-mode .

蓝牙高速(HS)

作为蓝牙3.0(2009)的可选功能引入,它通过提供 IEEE 802.11 (WiFi)作为另一种更高速的数据传输方式。节点协商切换 AMP .

这仅受标记为的蓝牙接口支持 +HS . 并非所有蓝牙3.0及更高版本的接口都支持它。

蓝牙低能量(BLE)

在蓝牙4.0(2010)中引入,这是为低功耗嵌入式系统设计的备用物理层。它具有更短的设置时间、更低的数据速率和更小的 MTU 尺寸。除了点对点链接之外,它还添加了广播和网状网络拓扑。

这只受标记为的蓝牙接口支持 +LE低能 --并非所有的蓝牙4.0和更高版本的接口都支持它。

PC上的大多数蓝牙接口都使用USB连接(甚至在笔记本电脑上),这是由主机控制器接口(HCI)控制的。这通常不支持混杂模式(嗅探),但是有许多其他专用的、非HCI设备支持它。

蓝牙插座 (AF_BLUETOOTH

蓝牙有多种协议可通过 AF_BLUETOOTH 插座:

主机控制器接口(HCI) BTPROTO_HCI

ScPy类: BluetoothHCISocket

这是用于与蓝牙控制器通信的“基本”级接口。一切都是建立在这个之上的,这代表着接近物理层,就像一个人可以得到常规的蓝牙硬件一样。

逻辑链路控制和适配层协议(L2CAP) BTPROTO_L2CAP

ScPy类: BluetoothL2CAPSocket

它位于HCI之上,为更高级别的协议提供连接和无连接的数据传输。它提供协议复用、包分割和重新组装操作。

当与单个设备通信时,可以使用L2CAP信道。

RFCOMM BluetoothRFCommSocket

ScPy类: BluetoothRFCommSocket

rfcomm是一种串行端口仿真协议,在L2CAP上运行。

除常规数据传输外,它还支持对所有RS-232非数据控制电路的操作。 (RTSDTR 等)

Linux上的蓝牙

Linux的蓝牙栈是由 `the BlueZ project`_ _. `The Linux kernel contains drivers to provide access to Bluetooth`_ _使用HCI的接口,这些接口通过 AF_BLUETOOTH .

Bluez还为这些内核接口提供了一个用户空间伙伴。关键组成部分包括:

bluetoothd
提供通过D总线访问蓝牙设备的守护进程。
bluetoothctl
一个交互式命令行程序,它与 bluetoothd 超过D总线。
hcitool
直接与内核接口接口的命令行程序。

Support for Classic Bluetooth in bluez is quite mature 然而, `BLE is under active development`_ _.

第一步

注解

您必须将这些示例运行为 root . 这些只在Linux上测试过,需要scapy 2.4.3或更高版本。

验证蓝牙设备

在执行其他操作之前,您需要检查您的蓝牙设备是否已被操作系统检测到:

$ hcitool dev
Devices:
        hci0 xx:xx:xx:xx:xx:xx

打开HCI插座

scapy的第一步是打开基础蓝牙设备的HCI插座:

>>> # Open a HCI socket to device hci0
>>> bt = BluetoothHCISocket(0)

发送控制包

此数据包不包含任何操作(即:它不执行任何操作),但它将测试您是否可以通过HCI设备进行通信:

>>> ans, unans = bt.sr(HCI_Hdr()/HCI_Command_Hdr())
Received 1 packets, got 1 answers, remaining 0 packets

然后可以检查响应:

>>> # ans[0] = Answered packet #0
>>> # ans[0][1] = The response packet
>>> ans[0][1]
>>> p.show()
###[ HCI header ]###
  type= Event
###[ HCI Event header ]###
     code= 0xf
     len= 4
###[ Command Status ]###
        status= 1
        number= 2
        opcode= 0x0

接收所有事件

要开始从HCI设备捕获所有事件,请使用 sniff

>>> pkts = bt.sniff()
(press ^C after a few seconds to stop...)
>>> pkts
<Sniffed: TCP:0 UDP:0 ICMP:0 Other:0>

除非你的电脑用蓝牙做了其他的事情,否则你可能会得到0个数据包。这是因为 sniff 实际上不会在设备上启用任何混乱模式。

但是,这对于稍后将解释的其他一些命令很有用。

导入和导出数据包

Just like with other protocols ,您可以保存数据包以备将来在 libpcap 格式与 wrpcap

>>> wrpcap("/tmp/bluetooth.pcap", pkts)

再装上 rdpcap

>>> pkts = rdpcap("/tmp/bluetooth.pcap")

使用蓝牙低能耗

注解

这需要支持蓝牙4.0或更高版本的接口 BLE, either as a dedicated LE chipset or a dual-mode LE + BR/EDR 芯片组(如 `RTL8723BU`_ ②)

这些指令只在Linux上测试过,需要scapy 2.4.3或更高版本。早期版本中存在错误解码数据包。

这些例子假设你已经 opened a HCI socket (AS) bt

发现附近的设备

启用发现模式

使用以下选项启动活动发现模式:

>>> # type=1: Active scanning mode
>>> bt.sr(
...   HCI_Hdr()/
...   HCI_Command_Hdr()/
...   HCI_Cmd_LE_Set_Scan_Parameters(type=1))
Received 1 packets, got 1 answers, remaining 0 packets

>>> # filter_dups=False: Show duplicate advertising reports, because these
>>> # sometimes contain different data!
>>> bt.sr(
...   HCI_Hdr()/
...   HCI_Command_Hdr()/
...   HCI_Cmd_LE_Set_Scan_Enable(
...     enable=True,
...     filter_dups=False))
Received 1 packets, got 1 answers, remaining 0 packets

在后台,已经有HCI事件在等待套接字。你可以用 sniff

>>> # The lfilter will drop anything that's not an advertising report.
>>> adverts = bt.sniff(lfilter=lambda p: HCI_LE_Meta_Advertising_Reports in p)
(press ^C after a few seconds to stop...)
>>> adverts
<Sniffed: TCP:0 UDP:0 ICMP:0 Other:101>

一旦您拥有数据包,请使用以下命令禁用发现模式:

>>> bt.sr(
...   HCI_Hdr()/
...   HCI_Command_Hdr()/
...   HCI_Cmd_LE_Set_Scan_Enable(
...     enable=False))
Begin emission:
Finished sending 1 packets.
...*
Received 4 packets, got 1 answers, remaining 0 packets
(<Results: TCP:0 UDP:0 ICMP:0 Other:1>, <Unanswered: TCP:0 UDP:0 ICMP:0 Other:0>)

收集广告报告

有时你可以得到多重的 HCI_LE_Meta_Advertising_Report 在单一 HCI_LE_Meta_Advertising_Reports 也可以用于不同的设备!

# Rearrange into a generator that returns reports sequentially
from itertools import chain
reports = chain.from_iterable(
  p[HCI_LE_Meta_Advertising_Reports].reports
  for p in adverts)

# Group reports by MAC address (consumes the reports generator)
devices = {}
for report in reports:
  device = devices.setdefault(report.addr, [])
  device.append(report)

# Packet counters
devices_pkts = dict((k, len(v)) for k, v in devices.items())
print(devices_pkts)
# {'xx:xx:xx:xx:xx:xx': 408, 'xx:xx:xx:xx:xx:xx': 2}

筛选广告报告

# Get one packet for each device that broadcasted short UUID 0xfe50 (Google).
# Android devices broadcast this pretty much constantly.
google = {}
for mac, reports in devices.items():
  for report in reports:
    if (EIR_CompleteList16BitServiceUUIDs in report and
        0xfe50 in report[EIR_CompleteList16BitServiceUUIDs].svc_uuids):
      google[mac] = report
      break

# List MAC addresses that sent such a broadcast
print(google.keys())
# dict_keys(['xx:xx:xx:xx:xx:xx', 'xx:xx:xx:xx:xx:xx'])

看看第一次收到的广播:

>>> for mac, report in google.items():
...   report.show()
...   break
...
###[ Advertising Report ]###
  type= conn_und
  atype= random
  addr= xx:xx:xx:xx:xx:xx
  len= 13
  \data\
   |###[ EIR Header ]###
   |  len= 2
   |  type= flags
   |###[ Flags ]###
   |     flags= general_disc_mode
   |###[ EIR Header ]###
   |  len= 3
   |  type= complete_list_16_bit_svc_uuids
   |###[ Complete list of 16-bit service UUIDs ]###
   |     svc_uuids= [0xfe50]
   |###[ EIR Header ]###
   |  len= 5
   |  type= svc_data_16_bit_uuid
   |###[ EIR Service Data - 16-bit UUID ]###
   |     svc_uuid= 0xfe50
   |     data= 'AB'
  rssi= -96

设置广告

注解

更改广告在第一次更改之前可能不会生效。 stopped .

Eddystone URL信标

此示例设置虚拟 `Eddystone URL`_ 灯塔:

# Load the contrib module for Eddystone
load_contrib('eddystone')

# Eddystone_URL.from_url() builds an Eddystone_URL frame for a given URL.
#
# build_set_advertising_data() wraps an Eddystone_Frame into a
# HCI_Cmd_LE_Set_Advertising_Data payload, that can be sent to the BLE
# controller.
bt.sr(Eddystone_URL.from_url(
  'https://scapy.net').build_set_advertising_data())

一次 advertising has been started ,然后可以用 `Eddystone Validator`_ (安卓):

_images/ble_eddystone_url.png

开始广告

bt.sr(HCI_Hdr()/
      HCI_Command_Hdr()/
      HCI_Cmd_LE_Set_Advertise_Enable(enable=True))

停止广告

bt.sr(HCI_Hdr()/
      HCI_Command_Hdr()/
      HCI_Cmd_LE_Set_Advertise_Enable(enable=False))

资源和参考资料